Compare commits

..

4 Commits

Author SHA1 Message Date
44ef355c54 [FEAT] 完善機台授權模組:新增搜尋過濾功能、機台資訊排版優化、更換圖格裝飾並完成後端效能優化。
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 49s
2026-03-30 13:48:22 +08:00
ea0333d77e [STYLE] 系統用語標準化與客戶管理 UI 點選透明度優化 (所屬單位 -> 公司名稱、姓名 -> 名稱)
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 59s
2026-03-30 10:16:05 +08:00
e780e195e2 style(UI): 微調放大客戶管理中業務類型的字體大小
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 56s
2026-03-30 09:14:52 +08:00
fdd3589d7b feat(Admin/Company): 擴充業務類型與合約期間功能,補齊多語系翻譯詞條
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m9s
2026-03-30 09:08:02 +08:00
17 changed files with 1070 additions and 877 deletions

View File

@@ -181,7 +181,7 @@ description: 定義 Star Cloud 管理後台的「極簡奢華風」設計規範
### 搜尋式下拉選單 (Searchable Select) - 【進階推薦】
- **組件**: `<x-searchable-select />`
- **適用場景**: 選項大於 10 筆或具備層級關聯的篩選器(如:所屬單位、機台編號)。
- **適用場景**: 選項大於 10 筆或具備層級關聯的篩選器(如:公司名稱、機台編號)。
- **奢華特徵**:
- **動態旋轉箭頭**: 透過 `::after` 偽元素實作,選單展開時箭頭執行 `300ms` 的 180 度旋轉動畫。
- **即時過濾**: 輸入關鍵字即時隱藏不匹配項。

View File

@@ -14,6 +14,7 @@ use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
use App\Models\System\User;
class MachineSettingController extends AdminController
{
@@ -45,7 +46,18 @@ class MachineSettingController extends AdminController
}
$models_list = $modelQuery->latest()->paginate($per_page)->withQueryString();
// 3. 基礎下拉資料 (用於新增/編輯機台的彈窗)
// 3. 處理使用者清單 (Accounts Tab - 授權帳號)
$userQuery = User::query()->with('machines')->whereNotNull('company_id'); // 僅列出租戶帳號以供分配
if ($tab === 'accounts' && $search) {
$userQuery->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('username', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
$users_list = $userQuery->latest()->paginate($per_page)->withQueryString();
// 4. 基礎下拉資料 (用於新增/編輯機台的彈窗)
$models = MachineModel::select('id', 'name')->get();
$paymentConfigs = PaymentConfig::select('id', 'name')->get();
$companies = \App\Models\System\Company::select('id', 'name', 'code')->get();
@@ -53,6 +65,7 @@ class MachineSettingController extends AdminController
return view('admin.basic-settings.machines.index', compact(
'machines',
'models_list',
'users_list',
'models',
'paymentConfigs',
'companies',
@@ -192,22 +205,83 @@ class MachineSettingController extends AdminController
public function regenerateToken(Request $request, $serial): \Illuminate\Http\JsonResponse
{
// 僅使用機台序號 (serial_no) 作為識別碼,最直覺且穩定
$machine = Machine::where('serial_no', $serial)->firstOrFail();
$newToken = \Illuminate\Support\Str::random(60);
$machine->update(['api_token' => $newToken]);
\Log::info('Machine API Token Regenerated', [
'machine_id' => $machine->id,
Log::info('Machine API Token Regenerated', [
'machine_id' => $machine->id,
'serial_no' => $machine->serial_no,
'user_id' => auth()->id()
]);
return response()->json([
'success' => true,
'api_token' => $newToken,
'message' => __('API Token regenerated successfully.')
'message' => __('API Token regenerated successfully.'),
'api_token' => $newToken
]);
}
/**
* AJAX: 取得特定帳號的機台分配狀態 ( MachineController 遷移)
*/
public function getAccountMachines(User $user): \Illuminate\Http\JsonResponse
{
$currentUser = auth()->user();
// 安全檢查:只能操作自己公司的帳號(除非是系統管理員)
if (!$currentUser->isSystemAdmin() && $user->company_id !== $currentUser->company_id) {
return response()->json(['error' => 'Unauthorized'], 403);
}
// 取得該使用者所屬公司之所有機台
$machines = Machine::where('company_id', $user->company_id)
->get(['id', 'name', 'serial_no']);
$assignedIds = $user->machines()->pluck('machines.id')->toArray();
return response()->json([
'user' => $user,
'machines' => $machines,
'assigned_ids' => $assignedIds
]);
}
/**
* AJAX: 儲存特定帳號的機台分配 ( MachineController 遷移)
*/
public function syncAccountMachines(Request $request, User $user): \Illuminate\Http\JsonResponse
{
$currentUser = auth()->user();
// 安全檢查
if (!$currentUser->isSystemAdmin() && $user->company_id !== $currentUser->company_id) {
return response()->json(['error' => 'Unauthorized'], 403);
}
$request->validate([
'machine_ids' => 'nullable|array',
'machine_ids.*' => 'exists:machines,id'
]);
// 加固驗證:確保所有機台 ID 都屬於該使用者的公司
if ($request->has('machine_ids')) {
$machineIds = array_unique($request->machine_ids);
$validCount = Machine::where('company_id', $user->company_id)
->whereIn('id', $machineIds)
->count();
if ($validCount !== count($machineIds)) {
return response()->json(['error' => 'Invalid machine IDs provided.'], 422);
}
}
$user->machines()->sync($request->machine_ids ?? []);
return response()->json([
'success' => true,
'message' => __('Permissions updated successfully'),
'assigned_machines' => $user->machines()->select('machines.id', 'machines.name', 'machines.serial_no')->get()
]);
}
}

View File

@@ -48,11 +48,13 @@ class CompanyController extends Controller
$validated = $request->validate([
'name' => 'required|string|max:255',
'code' => 'required|string|max:50|unique:companies,code',
'original_type' => 'required|string|in:buyout,lease',
'tax_id' => 'nullable|string|max:50',
'contact_name' => 'nullable|string|max:255',
'contact_phone' => 'nullable|string|max:50',
'contact_email' => 'nullable|email|max:255',
'valid_until' => 'nullable|date',
'start_date' => 'required|date',
'end_date' => 'nullable|date',
'status' => 'required|boolean',
'note' => 'nullable|string',
'settings' => 'nullable|array',
@@ -73,11 +75,14 @@ class CompanyController extends Controller
$company = Company::create([
'name' => $validated['name'],
'code' => $validated['code'],
'original_type' => $validated['original_type'],
'current_type' => $validated['original_type'], // 新增時同步
'tax_id' => $validated['tax_id'] ?? null,
'contact_name' => $validated['contact_name'] ?? null,
'contact_phone' => $validated['contact_phone'] ?? null,
'contact_email' => $validated['contact_email'] ?? null,
'valid_until' => $validated['valid_until'] ?? null,
'start_date' => $validated['start_date'] ?? null,
'end_date' => $validated['end_date'] ?? null,
'status' => $validated['status'],
'note' => $validated['note'] ?? null,
'settings' => $validated['settings'] ?? [],
@@ -131,11 +136,13 @@ class CompanyController extends Controller
$validated = $request->validate([
'name' => 'required|string|max:255',
'code' => 'required|string|max:50|unique:companies,code,' . $company->id,
'current_type' => 'required|string|in:buyout,lease',
'tax_id' => 'nullable|string|max:50',
'contact_name' => 'nullable|string|max:255',
'contact_phone' => 'nullable|string|max:50',
'contact_email' => 'nullable|email|max:255',
'valid_until' => 'nullable|date',
'start_date' => 'required|date',
'end_date' => 'nullable|date',
'status' => 'required|boolean',
'note' => 'nullable|string',
'settings' => 'nullable|array',

View File

@@ -102,69 +102,6 @@ class MachineController extends AdminController
}
/**
* AJAX: 取得特定帳號的機台分配狀態
*/
public function getAccountMachines(\App\Models\System\User $user)
{
$currentUser = auth()->user();
// 安全檢查:只能操作自己公司的帳號(除非是系統管理員)
if (!$currentUser->isSystemAdmin() && $user->company_id !== $currentUser->company_id) {
return response()->json(['error' => 'Unauthorized'], 403);
}
// 取得該公司所有機台 (限定 company_id 以實作資料隔離)
$machines = Machine::where('company_id', $user->company_id)
->get(['id', 'name', 'serial_no']);
$assignedIds = $user->machines()->pluck('machines.id')->toArray();
return response()->json([
'user' => $user,
'machines' => $machines,
'assigned_ids' => $assignedIds
]);
}
/**
* AJAX: 儲存特定帳號的機台分配
*/
public function syncAccountMachines(Request $request, \App\Models\System\User $user)
{
$currentUser = auth()->user();
// 安全檢查
if (!$currentUser->isSystemAdmin() && $user->company_id !== $currentUser->company_id) {
return response()->json(['error' => 'Unauthorized'], 403);
}
$request->validate([
'machine_ids' => 'nullable|array',
'machine_ids.*' => 'exists:machines,id'
]);
// 加固驗證:確保所有機台 ID 都屬於該使用者的公司
if ($request->has('machine_ids')) {
$machineIds = array_unique($request->machine_ids);
$validCount = Machine::where('company_id', $user->company_id)
->whereIn('id', $machineIds)
->count();
if ($validCount !== count($machineIds)) {
return response()->json(['error' => 'Invalid machine IDs provided.'], 422);
}
}
$user->machines()->sync($request->machine_ids ?? []);
return response()->json([
'success' => true,
'message' => __('Permissions updated successfully.'),
'assigned_machines' => $user->machines()->select('machines.id', 'machines.name', 'machines.serial_no')->get()
]);
}
/**
* 機台使用率統計
*/

View File

@@ -24,7 +24,7 @@ class PermissionController extends Controller
$query->where('name', 'like', "%{$search}%");
}
// 篩選:所屬單位 (僅限系統管理員)
// 篩選:公司名稱 (僅限系統管理員)
if ($user->isSystemAdmin() && request()->filled('company_id')) {
if (request()->company_id === 'system') {
$query->where('is_system', true);

View File

@@ -26,7 +26,7 @@ class EnsureTenantAccess
return redirect()->route('login')->with('error', __('Your account is associated with a deactivated company.'));
}
if ($company->valid_until && $company->valid_until->isPast()) {
if ($company->end_date && $company->end_date->isPast()) {
auth()->logout();
return redirect()->route('login')->with('error', __('Your company contract has expired.'));
}

View File

@@ -16,18 +16,22 @@ class Company extends Model
protected $fillable = [
'name',
'code',
'original_type',
'current_type',
'tax_id',
'contact_name',
'contact_phone',
'contact_email',
'status',
'valid_until',
'start_date',
'end_date',
'note',
'settings',
];
protected $casts = [
'valid_until' => 'date',
'start_date' => 'date',
'end_date' => 'date',
'status' => 'integer',
'settings' => 'array',
];

View File

@@ -0,0 +1,37 @@
<?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('companies', function (Blueprint $table) {
// 重新命名現有欄位
$table->renameColumn('valid_until', 'end_date');
// 新增業務類型
$table->string('original_type')->default('lease')->after('code')->comment('原始類型: buyout, lease');
$table->string('current_type')->default('lease')->after('original_type')->comment('當前類型: buyout, lease');
// 新增起始日
$table->date('start_date')->nullable()->after('status')->comment('合約起始日');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('companies', function (Blueprint $table) {
$table->renameColumn('end_date', 'valid_until');
$table->dropColumn(['original_type', 'current_type', 'start_date']);
});
}
};

View File

@@ -2,6 +2,8 @@
"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",
"API Token Copied": "API Token Copied",
"API Token regenerated successfully.": "API Token regenerated successfully.",
"APK Versions": "APK Versions",
"APP Features": "APP Features",
"APP Management": "APP Management",
@@ -9,6 +11,7 @@
"APP_ID": "APP_ID",
"APP_KEY": "APP_KEY",
"Account": "帳號",
"Account :name status has been changed to :status.": "Account :name status has been changed to :status.",
"Account Management": "Account Management",
"Account Name": "帳號姓名",
"Account Settings": "Account Settings",
@@ -34,13 +37,13 @@
"Admin display name": "Admin display name",
"Administrator": "Administrator",
"Advertisement Management": "Advertisement Management",
"Affiliated Unit": "Affiliated Unit",
"Affiliation": "Affiliation",
"Affiliated Unit": "Company Name",
"Affiliation": "Company Name",
"Alert Summary": "Alert Summary",
"Alerts": "中心告警",
"Alerts Pending": "Alerts Pending",
"All": "All",
"All Affiliations": "All Affiliations",
"All Affiliations": "All Companies",
"All Categories": "All Categories",
"All Companies": "All Companies",
"All Levels": "All Levels",
@@ -51,34 +54,48 @@
"Analysis Permissions": "Analysis Permissions",
"Apply 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 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.": "Are you sure you want to change the status of this product? Disabled products will not be visible on the machine.",
"Are you sure you want to change the status? After disabling, this account will no longer be able to log in to the system.": "Are you sure you want to change the status? After disabling, this account will no longer be able to log in to the system.",
"Are you sure you want to change the status? This may affect associated accounts.": "Are you sure you want to change the status? This may affect associated accounts.",
"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 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?",
"Are you sure you want to delete this account? This action cannot be undone.": "Are you sure you want to delete this account? This action cannot be undone.",
"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 configuration? This action cannot be undone.",
"Are you sure you want to delete this item? This action cannot be undone.": "Are you sure you want to delete this item? This action cannot be undone.",
"Are you sure you want to delete this product?": "Are you sure you want to delete this product?",
"Are you sure you want to delete this product? All related historical translation data will also be removed.": "Are you sure you want to delete this product? All related historical translation data will also be removed.",
"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?": "Are you sure?",
"Assign": "Assign",
"Assign Machines": "分配機台",
"Assigned Machines": "授權機台",
"Assign Machines": "Assign Machines",
"Assigned Machines": "Assigned Machines",
"Audit Management": "Audit Management",
"Audit Permissions": "Audit Permissions",
"Authorize": "Authorize",
"Authorized Accounts": "Authorized Accounts",
"Authorized Machines": "Authorized Machines",
"Authorized Machines Management": "Authorized Machines Management",
"Availability": "可用性 (Availability)",
"Available Machines": "可供分配的機台",
"Avatar updated successfully.": "Avatar updated successfully.",
"Avg Cycle": "Avg Cycle",
"Badge Settings": "Badge Settings",
"Barcode": "Barcode",
"Basic Information": "Basic Information",
"Basic Settings": "Basic Settings",
"Basic Specifications": "Basic Specifications",
"Batch No": "Batch No",
"Belongs To": "Belongs To",
"Belongs To Company": "Belongs To Company",
"Belongs To": "Company Name",
"Belongs To Company": "Company Name",
"Business Type": "Business Type",
"Buyout": "Buyout",
"Cancel": "Cancel",
"Cancel Purchase": "Cancel Purchase",
"Cannot Delete Role": "Cannot Delete Role",
"Cannot change Super Admin status.": "Cannot change Super Admin status.",
"Cannot delete company with active accounts.": "Cannot delete company with active accounts.",
"Cannot delete model that is currently in use by machines.": "Cannot delete model that is currently in use by machines.",
"Cannot delete role with active users.": "Cannot delete role with active users.",
"Card Reader": "Card Reader",
@@ -88,6 +105,8 @@
"Category": "Category",
"Change": "Change",
"Change Stock": "Change Stock",
"Channel Limits": "Channel Limits",
"Channel Limits Configuration": "Channel Limits Configuration",
"ChannelId": "ChannelId",
"ChannelSecret": "ChannelSecret",
"Checkout Time 1": "Checkout Time 1",
@@ -105,32 +124,50 @@
"Config Name": "配置名稱",
"Configuration Name": "Configuration Name",
"Confirm": "Confirm",
"Confirm Account Deactivation": "Confirm Deactivation",
"Confirm Account Status Change": "Confirm Account Status Change",
"Confirm Deletion": "Confirm Deletion",
"Confirm Password": "Confirm Password",
"Confirm Status Change": "Confirm Status Change",
"Connecting...": "Connecting...",
"Connectivity Status": "Connectivity Status",
"Connectivity vs Sales Correlation": "連線狀態與銷售關聯分析",
"Contact Info": "Contact Info",
"Contact & Details": "Contact & Details",
"Contact Email": "Contact Email",
"Contact Info": "Contact Info",
"Contact Name": "Contact Name",
"Contact Phone": "Contact Phone",
"Contract Period": "Contract Period",
"Contract Until (Optional)": "Contract Until (Optional)",
"Cost": "Cost",
"Coupons": "Coupons",
"Create": "Create",
"Create Config": "Create Config",
"Create Machine": "Create Machine",
"Create New Role": "Create New Role",
"Create Payment Config": "Create Payment Config",
"Create Product": "Create Product",
"Create Role": "Create Role",
"Create Sub Account Role": "Create Sub Account Role",
"Create a new role and assign permissions.": "Create a new role and assign permissions.",
"Critical": "Critical",
"Current": "Current",
"Current Password": "Current Password",
"Current Status": "Current Status",
"Current Stock": "Current Stock",
"Current Type": "Current Type",
"Current:": "Cur:",
"Customer Details": "Customer Details",
"Customer Info": "Customer Info",
"Customer Management": "Customer Management",
"Customer Payment Config": "Customer Payment Config",
"Customer and associated accounts disabled successfully.": "Customer and associated accounts disabled successfully.",
"Customer created successfully.": "Customer created successfully",
"Customer deleted successfully.": "Customer deleted successfully.",
"Customer enabled successfully.": "Customer enabled successfully.",
"Customer updated successfully.": "Customer updated successfully.",
"Cycle Efficiency": "Cycle Efficiency",
"Daily Revenue": "Daily Revenue",
"Danger Zone: Delete Account": "Danger Zone: Delete Account",
"Dashboard": "Dashboard",
"Data Configuration": "Data Configuration",
@@ -140,16 +177,19 @@
"Default Not Donate": "Default Not Donate",
"Define and manage security roles and permissions.": "Define and manage security roles and permissions.",
"Define new third-party payment parameters": "Define new third-party payment parameters",
"Feature Toggles": "Feature Toggles",
"Delete": "Delete",
"Delete Account": "Delete Account",
"Delete Permanently": "Delete Permanently",
"Delete Product Confirmation": "Delete Product Confirmation",
"Deposit Bonus": "Deposit Bonus",
"Describe the repair or maintenance status...": "Describe the repair or maintenance status...",
"Deselect All": "取消全選",
"Detail": "Detail",
"Device Information": "Device Information",
"Device Status Logs": "Device Status Logs",
"Devices": "Devices",
"Disable": "Disable",
"Disable Product Confirmation": "Disable Product Confirmation",
"Disabled": "Disabled",
"Discord Notifications": "Discord Notifications",
"Dispense Failed": "Dispense Failed",
@@ -168,11 +208,18 @@
"Edit Machine Model": "Edit Machine Model",
"Edit Machine Settings": "編輯機台設定",
"Edit Payment Config": "Edit Payment Config",
"Edit Product": "Edit Product",
"Edit Role": "Edit Role",
"Edit Role Permissions": "Edit Role Permissions",
"Edit Settings": "Edit Settings",
"Edit Sub Account Role": "Edit Sub Account Role",
"Email": "Email",
"Enable": "Enable",
"Enable Material Code": "Enable Material Code",
"Enable Points": "Enable Points",
"Enabled": "Enabled",
"Enabled/Disabled": "Enabled/Disabled",
"End Date": "End Date",
"Engineer": "Engineer",
"Ensure your account is using a long, random password to stay secure.": "Ensure your account is using a long, random password to stay secure.",
"Enter login ID": "Enter login ID",
@@ -184,26 +231,35 @@
"Enter your password to confirm": "Enter your password to confirm",
"Equipment efficiency and OEE metrics": "設備效能與 OEE 綜合指標",
"Error": "Error",
"Error processing request": "Error processing request",
"Execution Time": "Execution Time",
"Expired": "Expired",
"Expired / Disabled": "Expired / Disabled",
"Expiry Date": "Expiry Date",
"Expiry Management": "Expiry Management",
"Failed to fetch machine data.": "Failed to fetch machine data.",
"Failed to load permissions": "Failed to load permissions",
"Failed to save permissions.": "Failed to save permissions.",
"Failed to update machine images: ": "Failed to update machine images: ",
"Feature Settings": "Feature Settings",
"Feature Toggles": "Feature Toggles",
"Fill in the device repair or maintenance details": "Fill in the device repair or maintenance details",
"Fill in the product details below": "Fill in the product details below",
"Firmware Version": "Firmware Version",
"Fleet Avg OEE": "Fleet Avg OEE",
"Fleet Performance": "Fleet Performance",
"From": "From",
"From:": "From:",
"Full Access": "Full Access",
"Full Name": "Full Name",
"Full Points": "Full Points",
"Games": "Games",
"General permissions not linked to a specific menu.": "未連結到特定選單的一般權限。",
"Gift Definitions": "Gift Definitions",
"Global roles accessible by all administrators.": "Global roles accessible by all administrators.",
"Got it": "Got it",
"Half Points": "Half Points",
"Half Points Amount": "Half Points Amount",
"Hardware & Network": "Hardware & Network",
"Hardware & Slots": "Hardware & Slots",
"HashIV": "HashIV",
@@ -212,33 +268,9 @@
"Heating End Time": "Heating End Time",
"Heating Range": "Heating Range",
"Heating Start Time": "Heating Start Time",
"Channel Limits": "Channel Limits",
"Helper": "Helper",
"Home Page": "Home Page",
"Machine Utilization": "Machine Utilization",
"Machine Registry": "Machine Registry",
"Avg Cycle": "Avg Cycle",
"Cycle Efficiency": "Cycle Efficiency",
"Daily Revenue": "Daily Revenue",
"Real-time fleet efficiency and OEE metrics": "Real-time fleet efficiency and OEE metrics",
"Real-time performance analytics": "Real-time performance analytics",
"Reporting Period": "Reporting Period",
"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",
"Search serial or name...": "Search serial or name...",
"Serial NO": "Serial NO",
"OEE Efficiency Trend": "OEE Efficiency Trend",
"Online Status": "Online Status",
"Optimized Performance": "Optimized Performance",
"Output Count": "Output Count",
"OEE": "OEE",
"Utilized Time": "Utilized Time",
"Units": "Units",
"No Machine Selected": "No Machine Selected",
"No matching machines": "No matching machines",
"min": "min",
"s": "s",
"Total Gross Value": "Total Gross Value",
"Identity & Codes": "Identity & Codes",
"Info": "Info",
"Initial Admin Account": "Initial Admin Account",
"Initial Role": "Initial Role",
@@ -249,7 +281,6 @@
"Joined": "Joined",
"Key": "Key",
"Key No": "Key No",
"Identity & Codes": "Identity & Codes",
"LEVEL TYPE": "LEVEL TYPE",
"LINE Pay Direct": "LINE Pay Direct",
"LINE Pay Direct Settings Description": "LINE Pay Official Direct Connection Settings",
@@ -260,6 +291,7 @@
"Last Signal": "Last Signal",
"Last Time": "Last Time",
"Last Updated": "Last Updated",
"Lease": "Lease",
"Level": "Level",
"Line Coupons": "Line Coupons",
"Line Machines": "Line Machines",
@@ -269,14 +301,15 @@
"Line Orders": "Line Orders",
"Line Permissions": "Line Permissions",
"Line Products": "Line Products",
"Live Fleet Updates": "Live Fleet Updates",
"Loading machines...": "Loading machines...",
"Loyalty & Features": "Loyalty & Features",
"Loading...": "Loading...",
"Location": "Location",
"Locked Page": "Locked Page",
"Login History": "Login History",
"Logout": "Logout",
"Logs": "Logs",
"Loyalty & Features": "Loyalty & Features",
"Machine Count": "Machine Count",
"Machine Details": "Machine Details",
"Machine Images": "Machine Images",
@@ -291,12 +324,14 @@
"Machine Model Settings": "Machine Model Settings",
"Machine Name": "Machine Name",
"Machine Permissions": "Machine Permissions",
"Machine Registry": "Machine Registry",
"Machine Reports": "Machine Reports",
"Machine Restart": "Machine Restart",
"Machine Settings": "Machine Settings",
"Machine Status": "Machine Status",
"Machine Status List": "Machine Status List",
"Machine Stock": "Machine Stock",
"Machine Utilization": "Machine Utilization",
"Machine created successfully.": "Machine created successfully.",
"Machine images updated successfully.": "Machine images updated successfully.",
"Machine model created successfully.": "Machine model created successfully.",
@@ -313,21 +348,23 @@
"Maintenance QR": "Maintenance QR",
"Maintenance QR Code": "Maintenance QR Code",
"Maintenance Records": "Maintenance Records",
"Live Fleet Updates": "Live Fleet Updates",
"Maintenance record created successfully": "Maintenance record created successfully",
"Scan this code to quickly access the maintenance form for this device.": "Scan this code to quickly access the maintenance form for this device.",
"Manage Account Access": "管理帳號存取",
"Manage Expiry": "Manage Expiry",
"Manage administrative and tenant accounts": "Manage administrative and tenant accounts",
"Manage all tenant accounts and validity": "Manage all tenant accounts and validity",
"Manage your catalog, prices, and multilingual details.": "Manage your catalog, prices, and multilingual details.",
"Manage your machine fleet and operational data": "Manage your machine fleet and operational data",
"Manage your profile information, security settings, and login history": "Manage your profile information, security settings, and login history",
"Management of operational parameters": "Management of operational parameters",
"Management of operational parameters and models": "Management of operational parameters and models",
"Manufacturer": "Manufacturer",
"Material Code": "Material Code",
"Max 3": "Max 3",
"Member & External": "Member & External",
"Member List": "Member List",
"Member Management": "Member Management",
"Member Price": "Member Price",
"Member System": "Member System",
"Membership Tiers": "Membership Tiers",
"Menu Permissions": "Menu Permissions",
@@ -345,13 +382,18 @@
"Monthly Transactions": "Monthly Transactions",
"Monthly cumulative revenue overview": "Monthly cumulative revenue overview",
"Name": "Name",
"Name in English": "Name in English",
"Name in Japanese": "Name in Japanese",
"Name in Traditional Chinese": "Name in Traditional Chinese",
"Never Connected": "Never Connected",
"New Password": "New Password",
"New Password (leave blank to keep current)": "New Password (leave blank to keep current)",
"New Record": "New Record",
"New Role": "New Role",
"New Sub Account Role": "New Sub Account Role",
"Next": "Next",
"No Invoice": "No Invoice",
"No Machine Selected": "No Machine Selected",
"No accounts found": "No accounts found",
"No alert summary": "No alert summary",
"No configurations found": "No configurations found",
@@ -367,6 +409,7 @@
"No machines available": "No machines available",
"No machines available in this company.": "此客戶目前沒有可供分配的機台。",
"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 slots found": "No slots found",
@@ -376,6 +419,8 @@
"Not Used": "Not Used",
"Not Used Description": "不使用第三方支付介接",
"Notes": "Notes",
"OEE": "OEE",
"OEE Efficiency Trend": "OEE Efficiency Trend",
"OEE Score": "OEE Score",
"OEE.Activity": "Activity",
"OEE.Errors": "Errors",
@@ -389,19 +434,26 @@
"Online": "Online",
"Online Duration": "Online Duration",
"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.",
"Operational Parameters": "Operational Parameters",
"Operations": "Operations",
"Optimal": "Optimal",
"Optimized Performance": "Optimized Performance",
"Optimized for display. Supported formats: JPG, PNG, WebP.": "Optimized for display. Supported formats: JPG, PNG, WebP.",
"Optional": "Optional",
"Order Management": "Order Management",
"Orders": "Orders",
"Original": "Original",
"Original Type": "Original Type",
"Original:": "Ori:",
"Other Permissions": "Other Permissions",
"Others": "Others",
"Owner": "Owner",
"Output Count": "Output Count",
"Owner": "Company Name",
"PARTNER_KEY": "PARTNER_KEY",
"PI_MERCHANT_ID": "PI_MERCHANT_ID",
"PNG, JPG up to 2MB": "PNG, JPG up to 2MB",
"PS_MERCHANT_ID": "PS_MERCHANT_ID",
"Parameters": "Parameters",
"Pass Code": "Pass Code",
@@ -417,12 +469,12 @@
"Payment Configuration updated successfully.": "Payment Configuration updated successfully.",
"Payment Selection": "Payment Selection",
"Pending": "Pending",
"Pricing Information": "Pricing Information",
"Performance": "Performance",
"Permanent": "Permanent",
"Permanently Delete Account": "Permanently Delete Account",
"Permission Settings": "Permission Settings",
"Permissions": "Permissions",
"Permissions updated successfully": "Authorization updated successfully",
"Phone": "Phone",
"Photo Slot": "Photo Slot",
"Pickup Code": "Pickup Code",
@@ -433,9 +485,18 @@
"Point Rules": "Point Rules",
"Point Settings": "Point Settings",
"Previous": "Previous",
"Pricing Information": "Pricing Information",
"Product Details": "Product Details",
"Product Image": "Product Image",
"Product Management": "Product Management",
"Product Name (Multilingual)": "Product Name (Multilingual)",
"Product Reports": "Product Reports",
"Product Status": "商品狀態",
"Product created successfully": "Product created successfully",
"Product deleted successfully": "Product deleted successfully",
"Product status updated to :status": "Product status updated to :status",
"Product updated successfully": "Product updated successfully",
"Production Company": "Production Company",
"Profile": "Profile",
"Profile Information": "Profile Information",
"Profile Settings": "Profile Settings",
@@ -453,10 +514,14 @@
"Quick search...": "Quick search...",
"Real-time OEE analysis awaits": "即時 OEE 分析預備中",
"Real-time Operation Logs (Last 50)": "Real-time Operation Logs (Last 50)",
"Real-time fleet efficiency and OEE metrics": "Real-time fleet efficiency and OEE metrics",
"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",
"Receipt Printing": "Receipt Printing",
"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 Dispense": "Remote Dispense",
@@ -469,6 +534,7 @@
"Replenishment Page": "Replenishment Page",
"Replenishment Records": "Replenishment Records",
"Replenishments": "Replenishments",
"Reporting Period": "Reporting Period",
"Reservation Members": "Reservation Members",
"Reservation System": "Reservation System",
"Reservations": "Reservations",
@@ -490,6 +556,7 @@
"Roles scoped to specific customer companies.": "Roles scoped to specific customer companies.",
"Running Status": "Running Status",
"SYSTEM": "SYSTEM",
"Sale Price": "Sale Price",
"Sales": "Sales",
"Sales Activity": "銷售活動",
"Sales Management": "Sales Management",
@@ -502,28 +569,36 @@
"Saved.": "Saved.",
"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 customers...": "Search customers...",
"Search machines by name or serial...": "Search machines by name or serial...",
"Search machines...": "Search machines...",
"Search machines...": "Search machine name or serial...",
"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...",
"Select All": "全選",
"Select Company": "Select Company",
"Select Company": "Select Company Name",
"Select Machine": "選擇機台",
"Select Machine to view metrics": "請選擇機台以查看指標",
"Select Model": "Select Model",
"Select Owner": "Select Owner",
"Select Owner": "Select Company Name",
"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",
"Selected": "Selected",
"Selected Date": "查詢日期",
"Selection": "Selection",
"Serial & Version": "Serial & Version",
"Serial NO": "Serial NO",
"Serial No": "Serial No",
"Serial Number": "Serial Number",
"Show": "Show",
"Show material code field in products": "Show material code field in products",
"Show points rules in products": "Show points rules in products",
"Showing": "Showing",
"Showing :from to :to of :total items": "Showing :from to :to of :total items",
"Sign in to your account": "Sign in to your account",
@@ -534,7 +609,11 @@
"Slot Test": "Slot Test",
"Some fields need attention": "Some fields need attention",
"Special Permission": "Special Permission",
"Specifications": "Specifications",
"Spring Channel Limit": "Spring Channel Limit",
"Spring Limit": "Spring Limit",
"Staff Stock": "Staff Stock",
"Start Date": "Start Date",
"Status": "Status",
"Status / Temp / Sub / Card / Scan": "Status / Temp / Sub / Card / Scan",
"Stock Management": "Stock Management",
@@ -583,24 +662,30 @@
"Total Connected": "Total Connected",
"Total Customers": "Total Customers",
"Total Daily Sales": "Today's Total Sales",
"Total Gross Value": "Total Gross Value",
"Total Logins": "Total Logins",
"Total Selected": "Total Selected",
"Total Slots": "Total Slots",
"Total items": "Total items: :count",
"Track Channel Limit": "Track Channel Limit",
"Track Limit": "Track Limit",
"Track device health and maintenance history": "Track device health and maintenance history",
"Transfer Audit": "Transfer Audit",
"Transfers": "Transfers",
"Tutorial Page": "Tutorial Page",
"Type": "Type",
"UI Elements": "UI Elements",
"Uncategorized": "Uncategorized",
"Unified Operational Timeline": "Unified Operational Timeline",
"Units": "Units",
"Unknown": "Unknown",
"Update": "Update",
"Update Authorization": "Update Authorization",
"Update Customer": "Update Customer",
"Update Password": "Update Password",
"Update Product": "Update Product",
"Update existing role and permissions.": "Update existing role and permissions.",
"Update your account's profile information and email address.": "Update your account's profile information and email address.",
"Validation Error": "Validation Error",
"Upload New Images": "Upload New Images",
"Uploading new images will replace all existing images.": "Uploading new images will replace all existing images.",
"User": "User",
@@ -610,7 +695,9 @@
"Utilization Rate": "Utilization Rate",
"Utilization Timeline": "稼動時序",
"Utilization, OEE and Operational Intelligence": "稼動率、OEE 與營運情報",
"Utilized Time": "Utilized Time",
"Valid Until": "Valid Until",
"Validation Error": "Validation Error",
"Vending Page": "Vending Page",
"Venue Management": "Venue Management",
"View Details": "View Details",
@@ -626,6 +713,7 @@
"Welcome Gift": "Welcome Gift",
"Welcome Gift Status": "Welcome Gift Status",
"Work Content": "Work Content",
"Yes, regenerate": "Yes, regenerate",
"Yesterday": "Yesterday",
"You cannot assign permissions you do not possess.": "You cannot assign permissions you do not possess.",
"You cannot delete your own account.": "You cannot delete your own account.",
@@ -663,6 +751,11 @@
"menu.basic.machines": "Machine Settings",
"menu.basic.payment-configs": "Customer Payment Config",
"menu.data-config": "Data Configuration",
"menu.data-config.admin-products": "Product Status",
"menu.data-config.advertisements": "Advertisement Management",
"menu.data-config.badges": "Badge Settings",
"menu.data-config.points": "Point Settings",
"menu.data-config.products": "Product Management",
"menu.data-config.sub-account-roles": "Sub Account Roles",
"menu.data-config.sub-accounts": "Sub Account Management",
"menu.line": "Line Management",
@@ -681,6 +774,7 @@
"menu.sales": "Sales Management",
"menu.special-permission": "Special Permission",
"menu.warehouses": "Warehouse Management",
"min": "min",
"of": "of",
"permissions": "Permission Settings",
"permissions.accounts": "帳號管理",
@@ -689,6 +783,7 @@
"remote": "Remote Management",
"reservation": "Reservation System",
"roles": "Role Permissions",
"s": "s",
"sales": "Sales Management",
"special-permission": "Special Permission",
"super-admin": "超級管理員",
@@ -697,79 +792,9 @@
"vs Yesterday": "vs Yesterday",
"warehouses": "Warehouse Management",
"待填寫": "Pending",
"Enabled": "Enabled",
"Account :name status has been changed to :status.": "Account :name status has been changed to :status.",
"Cannot change Super Admin status.": "Cannot change Super Admin status.",
"Confirm Status Change": "Confirm Status Change",
"Disable Product Confirmation": "Disable Product Confirmation",
"Delete Product Confirmation": "Delete Product Confirmation",
"Are you sure you want to change the status of this product? Disabled products will not be visible on the machine.": "Are you sure you want to change the status of this product? Disabled products will not be visible on the machine.",
"Are you sure you want to delete this product? All related historical translation data will also be removed.": "Are you sure you want to delete this product? All related historical translation data will also be removed.",
"Are you sure you want to change the status? This may affect associated accounts.": "Are you sure you want to change the status? This may affect associated accounts.",
"Confirm Account Status Change": "Confirm Account Status Change",
"Are you sure you want to change the status? After disabling, this account will no longer be able to log in to the system.": "Are you sure you want to change the status? After disabling, this account will no longer be able to log in to the system.",
"Confirm Account Deactivation": "Confirm Deactivation",
"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 deactivate this account? After deactivating, this account will no longer be able to log in to the system.",
"Enable": "Enable",
"Disable": "Disable",
"Regenerate": "Regenerate",
"API Token Copied": "API Token Copied",
"Yes, regenerate": "Yes, 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?",
"Error processing request": "Error processing request",
"API Token regenerated successfully.": "API Token regenerated successfully.",
"Product Name (Multilingual)": "Product Name (Multilingual)",
"Material Code": "Material Code",
"Full Points": "Full Points",
"Half Points": "Half Points",
"Half Points Amount": "Half Points Amount",
"Name in Traditional Chinese": "Name in Traditional Chinese",
"Name in English": "Name in English",
"Name in Japanese": "Name in Japanese",
"Track Limit": "Track Limit",
"Spring Limit": "Spring Limit",
"Channel Limits Configuration": "Channel Limits Configuration",
"Manage your catalog, prices, and multilingual details.": "Manage your catalog, prices, and multilingual details.",
"Product created successfully": "Product created successfully",
"Product updated successfully": "Product updated successfully",
"Product deleted successfully": "Product deleted successfully",
"Fill in the product details below": "Fill in the product details below",
"Uncategorized": "Uncategorized",
"Track Channel Limit": "Track Channel Limit",
"Spring Channel Limit": "Spring Channel Limit",
"Product Details": "Product Details",
"Production Company": "Production Company",
"Barcode": "Barcode",
"Specifications": "Specifications",
"Cost": "Cost",
"Member Price": "Member Price",
"Sale Price": "Sale Price",
"Update Product": "Update Product",
"Edit Product": "Edit Product",
"Create Product": "Create Product",
"Are you sure you want to delete this product?": "Are you sure you want to delete this product?",
"Feature Settings": "Feature Settings",
"Enable Material Code": "Enable Material Code",
"Show material code field in products": "Show material code field in products",
"Enable Points": "Enable Points",
"Show points rules in products": "Show points rules in products",
"Customer Details": "Customer Details",
"Current Status": "Current Status",
"Product Image": "Product Image",
"PNG, JPG up to 2MB": "PNG, JPG up to 2MB",
"menu.data-config.products": "Product Management",
"menu.data-config.advertisements": "Advertisement Management",
"menu.data-config.admin-products": "Product Status",
"menu.data-config.points": "Point Settings",
"menu.data-config.badges": "Badge Settings",
"Create New Role": "Create New Role",
"Create Sub Account Role": "Create Sub Account Role",
"Edit Sub Account Role": "Edit Sub Account Role",
"New Role": "New Role",
"Manufacturer": "Manufacturer",
"Product status updated to :status": "Product status updated to :status",
"Customer and associated accounts disabled successfully.": "Customer and associated accounts disabled successfully.",
"Customer enabled successfully.": "Customer enabled successfully.",
"Cannot delete company with active accounts.": "Cannot delete company with active accounts.",
"Customer deleted successfully.": "Customer deleted successfully."
}
"Authorized Accounts Tab": "Authorized Accounts",
"Authorize Btn": "Authorize",
"Authorization updated successfully": "Authorization updated successfully",
"Authorized Status": "Authorized",
"Unauthorized Status": "Unauthorized"
}

View File

@@ -2,6 +2,8 @@
"A new verification link has been sent to your email address.": "新しい確認リンクがメールアドレスに送信されました。",
"AI Prediction": "AI予測",
"API Token": "API キー",
"API Token Copied": "APIトークンがコピーされました",
"API Token regenerated successfully.": "APIトークンが正常に再生成されました。",
"APK Versions": "APKバージョン",
"APP Features": "APP機能",
"APP Management": "APP管理",
@@ -9,6 +11,7 @@
"APP_ID": "APP_ID",
"APP_KEY": "APP_KEY",
"Account": "帳號",
"Account :name status has been changed to :status.": "アカウント :name のステータスが :status に変更されました。",
"Account Management": "アカウント管理",
"Account Name": "帳號姓名",
"Account Settings": "アカウント設定",
@@ -34,13 +37,13 @@
"Admin display name": "管理者表示名",
"Administrator": "管理者",
"Advertisement Management": "廣告管理",
"Affiliated Unit": "所属ユニット",
"Affiliation": "所属",
"Affiliated Unit": "会社名",
"Affiliation": "会社名",
"Alert Summary": "アラート概要",
"Alerts": "中心告警",
"Alerts Pending": "アラート待機中",
"All": "すべて",
"All Affiliations": "全ての所属",
"All Affiliations": "すべての会社",
"All Categories": "すべてのカテゴリ",
"All Companies": "すべての会社",
"All Levels": "すべてのレベル",
@@ -51,11 +54,17 @@
"Analysis Permissions": "分析管理權限",
"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.": "この商品のステータスを変更してもよろしいですか?無効にされた商品はマシンに表示されません。",
"Are you sure you want to change the status? After disabling, this account will no longer be able to log in to the system.": "ステータスを変更してもよろしいですか?無効化後、このアカウントはシステムにログインできなくなります。",
"Are you sure you want to change the status? This may affect associated accounts.": "ステータスを変更してもよろしいですか?関連するアカウントに影響する可能性があります。",
"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 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.": "この項目を削除してもよろしいですか?この操作は元に戻せません。",
"Are you sure you want to delete this product?": "この商品を削除してもよろしいですか?",
"Are you sure you want to delete this product? All related historical translation data will also be removed.": "この商品を削除してもよろしいですか?関連するすべての履歴翻訳データも削除されます。",
"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.": "您確定要繼續嗎?此操作將無法復原。",
@@ -69,16 +78,22 @@
"Availability": "可用性 (Availability)",
"Available Machines": "可供分配的機台",
"Avatar updated successfully.": "アバターが正常に更新されました。",
"Avg Cycle": "平均サイクル",
"Badge Settings": "バッジ設定",
"Barcode": "バーコード",
"Basic Information": "基本情報",
"Basic Settings": "基本設定",
"Basic Specifications": "基本仕様",
"Batch No": "批號",
"Belongs To": "所属",
"Belongs To Company": "所属会社",
"Belongs To": "会社名",
"Belongs To Company": "会社",
"Business Type": "業務タイプ",
"Buyout": "買取",
"Cancel": "キャンセル",
"Cancel Purchase": "購入キャンセル",
"Cannot Delete Role": "ロールを削除できません",
"Cannot change Super Admin status.": "スーパー管理者のステータスは変更できません。",
"Cannot delete company with active accounts.": "有効なアカウントを持つ顧客を削除できません。",
"Cannot delete model that is currently in use by machines.": "機台で使用中の型號は削除できません。",
"Cannot delete role with active users.": "アクティブなユーザーがいるロールは削除できません。",
"Card Reader": "カードリーダー",
@@ -88,6 +103,8 @@
"Category": "カテゴリ",
"Change": "変更",
"Change Stock": "小銭在庫",
"Channel Limits": "スロット上限",
"Channel Limits Configuration": "スロット上限設定",
"ChannelId": "チャンネルID",
"ChannelSecret": "チャンネルシークレット",
"Checkout Time 1": "決済時間 1",
@@ -105,32 +122,48 @@
"Config Name": "配置名稱",
"Configuration Name": "設定名称",
"Confirm": "確認",
"Confirm Account Deactivation": "アカウント停止の確認",
"Confirm Account Status Change": "アカウントステータス変更の確認",
"Confirm Deletion": "削除の確認",
"Confirm Password": "新しいパスワード(確認)",
"Confirm Status Change": "ステータス変更の確認",
"Connecting...": "接続中...",
"Connectivity Status": "接続ステータス概況",
"Connectivity vs Sales Correlation": "連線狀態與銷售關聯分析",
"Contact Info": "連絡先情報",
"Contact & Details": "連絡先と詳細",
"Contact Email": "連絡先メールアドレス",
"Contact Info": "連絡先情報",
"Contact Name": "連絡担当者名",
"Contact Phone": "連絡先電話番号",
"Contract Period": "契約期間",
"Contract Until (Optional)": "契約期限 (任意)",
"Cost": "原価",
"Coupons": "クーポン",
"Create": "作成",
"Create Config": "設定を新規作成",
"Create Machine": "機台新規作成",
"Create New Role": "新しいロールを作成",
"Create Payment Config": "決済設定を新規作成",
"Create Product": "商品を作成",
"Create Role": "ロール作成",
"Create Sub Account Role": "サブアカウントロールを作成",
"Create a new role and assign permissions.": "新しいロールを作成し、権限を割り当てます。",
"Critical": "致命的",
"Current": "現在",
"Current Password": "現在のパスワード",
"Current Stock": "現在の在庫",
"Current Type": "現タイプ",
"Current:": "現:",
"Customer Info": "顧客情報",
"Customer Management": "顧客管理",
"Customer Payment Config": "決済設定管理",
"Customer and associated accounts disabled successfully.": "顧客と関連アカウントが正常に無効化されました。",
"Customer created successfully.": "顧客が正常に作成されました。",
"Customer deleted successfully.": "顧客が正常に削除されました。",
"Customer enabled successfully.": "顧客が正常に有効化されました。",
"Customer updated successfully.": "顧客が正常に更新されました。",
"Cycle Efficiency": "サイクル効率",
"Daily Revenue": "日次収益",
"Danger Zone: Delete Account": "危険区域:アカウントの削除",
"Dashboard": "ダッシュボード",
"Data Configuration": "データ設定",
@@ -140,16 +173,19 @@
"Default Not Donate": "デフォルト寄付しない",
"Define and manage security roles and permissions.": "システムのセキュリティロールと権限を定義および管理します。",
"Define new third-party payment parameters": "新しいサードパーティ決済パラメータを定義",
"Feature Toggles": "機能トグル",
"Delete": "削除",
"Delete Account": "アカウントの削除",
"Delete Permanently": "完全に削除",
"Delete Product Confirmation": "商品削除の確認",
"Deposit Bonus": "入金ボーナス",
"Describe the repair or maintenance status...": "修理またはメンテナンスの状況を説明してください...",
"Deselect All": "取消全選",
"Detail": "詳細",
"Device Information": "デバイス情報",
"Device Status Logs": "デバイス状態ログ",
"Devices": "台のデバイス",
"Disable": "無効にする",
"Disable Product Confirmation": "商品無効化の確認",
"Disabled": "停止中",
"Discord Notifications": "Discord通知",
"Dispense Failed": "出庫失敗",
@@ -168,11 +204,18 @@
"Edit Machine Model": "機台型號を編輯",
"Edit Machine Settings": "編輯機台設定",
"Edit Payment Config": "決済設定を編集",
"Edit Product": "商品を編集",
"Edit Role": "ロール編集",
"Edit Role Permissions": "ロール権限の編集",
"Edit Settings": "設定編集",
"Edit Sub Account Role": "サブアカウントロールを編集",
"Email": "メールアドレス",
"Enable": "有効にする",
"Enable Material Code": "資材コードを有効化",
"Enable Points": "ポイントルールを有効化",
"Enabled": "有効中",
"Enabled/Disabled": "有効/無効",
"End Date": "終了日",
"Engineer": "メンテナンス担当者",
"Ensure your account is using a long, random password to stay secure.": "セキュリティを維持するため、アカウントには長くランダムなパスワードを使用してください。",
"Enter login ID": "ログインIDを入力してください",
@@ -184,26 +227,35 @@
"Enter your password to confirm": "確認のためパスワードを入力してください",
"Equipment efficiency and OEE metrics": "設備効率と OEE 指標",
"Error": "エラー",
"Error processing request": "リクエストの処理中にエラーが発生しました",
"Execution Time": "実行時間",
"Expired": "期限切れ",
"Expired / Disabled": "期限切れ / 停止中",
"Expiry Date": "有效日期",
"Expiry Management": "有効期限管理",
"Failed to fetch machine data.": "無法取得機台資料。",
"Failed to load permissions": "権限の読み込みに失敗しました",
"Failed to save permissions.": "無法儲存權限設定。",
"Failed to update machine images: ": "機台画像の更新に失敗しました:",
"Feature Settings": "機能設定",
"Feature Toggles": "機能トグル",
"Fill in the device repair or maintenance details": "デバイスの修理またはメンテナンスの詳細を入力してください",
"Fill in the product details below": "以下に商品の詳細を入力してください",
"Firmware Version": "ファームウェアバージョン",
"Fleet Avg OEE": "フリート平均 OEE",
"Fleet Performance": "全機隊效能",
"From": "から",
"From:": "開始:",
"Full Access": "全機台授權",
"Full Name": "氏名",
"Full Points": "フルポイント",
"Games": "ゲーム",
"General permissions not linked to a specific menu.": "未連結到特定選單的一般權限。",
"Gift Definitions": "ギフト設定",
"Global roles accessible by all administrators.": "適用於所有管理者的全域角色。",
"Got it": "了解",
"Half Points": "ハーフポイント",
"Half Points Amount": "ハーフポイント金額",
"Hardware & Network": "ハードウェアとネットワーク",
"Hardware & Slots": "ハードウェアと貨道",
"HashIV": "HashIV",
@@ -212,33 +264,9 @@
"Heating End Time": "加熱終了時間",
"Heating Range": "加熱時間帯",
"Heating Start Time": "加熱開始時間",
"Channel Limits": "スロット上限",
"Helper": "ヘルパー",
"Home Page": "主画面",
"Machine Utilization": "機台稼働率",
"Machine Registry": "機台登録",
"Avg Cycle": "平均サイクル",
"Cycle Efficiency": "サイクル効率",
"Daily Revenue": "日次収益",
"Real-time fleet efficiency and OEE metrics": "リアルタイムのフリート効率と OEE 指標",
"Real-time performance analytics": "リアルタイム・パフォーマンス分析",
"Reporting Period": "レポート期間",
"Select an asset from the left to start analysis": "分析を開始するには左側のデバイスを選択してください",
"Select date to sync data": "データ同期の日付を選択してください",
"Search serial or name...": "シリアル番号または名称で検索...",
"Serial NO": "シリアル番号",
"OEE Efficiency Trend": "OEE 効率トレンド",
"Online Status": "オンライン状態",
"Optimized Performance": "最適化されたパフォーマンス",
"Output Count": "出力数",
"OEE": "OEE",
"Utilized Time": "稼働時間",
"Units": "ユニット",
"No Machine Selected": "機台が選択されていません",
"No matching machines": "一致する機台が見つかりません",
"min": "分",
"s": "秒",
"Total Gross Value": "総売上額",
"Identity & Codes": "識別とコード",
"Info": "情報",
"Initial Admin Account": "初期管理者アカウント",
"Initial Role": "初期ロール",
@@ -249,7 +277,6 @@
"Joined": "入会日",
"Key": "キー (Key)",
"Key No": "キー番号",
"Identity & Codes": "識別とコード",
"LEVEL TYPE": "層級タイプ",
"LINE Pay Direct": "LINE Pay 直結決済",
"LINE Pay Direct Settings Description": "LINE Pay 公式直結設定",
@@ -260,6 +287,7 @@
"Last Signal": "最終信号時間",
"Last Time": "最終時間",
"Last Updated": "最終更新",
"Lease": "リース",
"Level": "レベル",
"Line Coupons": "Lineクーポン",
"Line Machines": "Line機台",
@@ -269,14 +297,15 @@
"Line Orders": "Line注文",
"Line Permissions": "Line管理權限",
"Line Products": "Line商品",
"Live Fleet Updates": "ライブフリート更新",
"Loading machines...": "正在載入機台...",
"Loyalty & Features": "ロイヤリティと機能",
"Loading...": "読み込み中...",
"Location": "場所",
"Locked Page": "ロック画面",
"Login History": "ログイン履歴",
"Logout": "ログアウト",
"Logs": "ログ",
"Loyalty & Features": "ロイヤリティと機能",
"Machine Count": "機台数量",
"Machine Details": "機台詳情",
"Machine Images": "機台写真",
@@ -291,12 +320,14 @@
"Machine Model Settings": "機台型號設定",
"Machine Name": "機台名",
"Machine Permissions": "機台権限",
"Machine Registry": "機台登録",
"Machine Reports": "機台レポート",
"Machine Restart": "機台再起動",
"Machine Settings": "機台設定",
"Machine Status": "機台状態",
"Machine Status List": "機台稼働状況リスト",
"Machine Stock": "機台在庫",
"Machine Utilization": "機台稼働率",
"Machine created successfully.": "機台が正常に作成されました。",
"Machine images updated successfully.": "機台画像が正常に更新されました。",
"Machine model created successfully.": "機台型號が正常に作成されました。",
@@ -313,21 +344,23 @@
"Maintenance QR": "メンテナンス QR",
"Maintenance QR Code": "メンテナンス QR コード",
"Maintenance Records": "メンテナンス記録",
"Live Fleet Updates": "ライブフリート更新",
"Maintenance record created successfully": "メンテナンス記録が正常に作成されました",
"Scan this code to quickly access the maintenance form for this device.": "このコードをスキャンして、このデバイスのメンテナンスフォームに素早くアクセスしてください。",
"Manage Account Access": "管理帳號存取",
"Manage Expiry": "進入效期管理",
"Manage administrative and tenant accounts": "管理者およびテナントアカウントを管理します",
"Manage all tenant accounts and validity": "すべてのテナントアカウントと有効期限を管理します",
"Manage your catalog, prices, and multilingual details.": "カタログ、価格、多言語詳細を管理します。",
"Manage your machine fleet and operational data": "機台フリートと運用データの管理",
"Manage your profile information, security settings, and login history": "プロフィール情報、セキュリティ設定、ログイン履歴の管理",
"Management of operational parameters": "機台運作參數管理",
"Management of operational parameters and models": "運用パラメータと型番の管理",
"Manufacturer": "製造元",
"Material Code": "物料コード",
"Max 3": "最大3枚",
"Member & External": "会員と外部システム",
"Member List": "会員リスト",
"Member Management": "会員管理",
"Member Price": "会員価格",
"Member System": "会員システム",
"Membership Tiers": "会員ランク",
"Menu Permissions": "メニュー権限",
@@ -345,13 +378,18 @@
"Monthly Transactions": "今月の取引統計",
"Monthly cumulative revenue overview": "今月の累計収益概要",
"Name": "氏名",
"Name in English": "英語名",
"Name in Japanese": "日本語名",
"Name in Traditional Chinese": "繁体字中国語名",
"Never Connected": "未接続",
"New Password": "新しいパスワード",
"New Password (leave blank to keep current)": "新しいパスワード (変更しない場合は空欄)",
"New Record": "新規記録",
"New Role": "新しいロール",
"New Sub Account Role": "新增子帳號角色",
"Next": "次へ",
"No Invoice": "発票を発行しない",
"No Machine Selected": "機台が選択されていません",
"No accounts found": "アカウントが見つかりません",
"No alert summary": "アラートなし",
"No configurations found": "設定が見つかりません",
@@ -368,6 +406,7 @@
"No machines available in this company.": "此客戶目前沒有可供分配的機台。",
"No maintenance records found": "メンテナンス記録が見つかりません",
"No matching logs found": "一致するログが見つかりません",
"No matching machines": "一致する機台が見つかりません",
"No permissions": "権限項目なし",
"No roles found.": "ロールが見つかりませんでした。",
"No slots found": "未找到貨道資訊",
@@ -377,6 +416,8 @@
"Not Used": "未使用",
"Not Used Description": "不使用第三方支付介接",
"Notes": "備考",
"OEE": "OEE",
"OEE Efficiency Trend": "OEE 効率トレンド",
"OEE Score": "OEE スコア",
"OEE.Activity": "稼働アクティビティ",
"OEE.Errors": "エラー",
@@ -390,19 +431,26 @@
"Online": "オンライン",
"Online Duration": "累積連線時數",
"Online Machines": "オンライン機台",
"Online Status": "オンライン状態",
"Only system roles can be assigned to platform administrative accounts.": "プラットフォーム管理アカウントにはシステムロールのみ割り当て可能です。",
"Operational Parameters": "運用パラメータ",
"Operations": "運用設定",
"Optimal": "最適",
"Optimized Performance": "最適化されたパフォーマンス",
"Optimized for display. Supported formats: JPG, PNG, WebP.": "表示用に最適化されています。対応形式JPG, PNG, WebP。",
"Optional": "任意",
"Order Management": "注文管理",
"Orders": "注文",
"Original": "最初",
"Original Type": "元タイプ",
"Original:": "元:",
"Other Permissions": "其他權限",
"Others": "その他",
"Owner": "所属会社",
"Output Count": "出力数",
"Owner": "会社名",
"PARTNER_KEY": "パートナーキー",
"PI_MERCHANT_ID": "Pi 拍錢包 加盟店ID",
"PNG, JPG up to 2MB": "PNG, JPG (最大 2MB)",
"PS_MERCHANT_ID": "全盈+Pay 加盟店ID",
"Parameters": "パラメータ設定",
"Pass Code": "パスコード",
@@ -418,13 +466,12 @@
"Payment Configuration updated successfully.": "決済設定が正常に更新されました。",
"Payment Selection": "決済選択",
"Pending": "保留中",
"Pricing Information": "価格情報",
"Channel Limits Configuration": "スロット上限設定",
"Performance": "パフォーマンス (Performance)",
"Permanent": "永久認可",
"Permanently Delete Account": "アカウントを永久に削除",
"Permission Settings": "権限設定",
"Permissions": "権限",
"Permissions updated successfully": "認証が更新されました",
"Phone": "電話番号",
"Photo Slot": "写真スロット",
"Pickup Code": "受取コード",
@@ -434,10 +481,22 @@
"Please select a machine to view metrics": "機台を選択して指標を表示してください",
"Point Rules": "ポイントルール",
"Point Settings": "ポイント設定",
"Points Rule": "ポイントルール",
"Points Settings": "ポイント設定",
"Points toggle": "ポイント切り替え",
"Previous": "前へ",
"Pricing Information": "価格情報",
"Product Details": "商品詳細",
"Product Image": "商品画像",
"Product Management": "商品管理",
"Product Name (Multilingual)": "商品名 (多言語)",
"Product Reports": "商品レポート",
"Product Status": "商品狀態",
"Product created successfully": "商品が正常に作成されました",
"Product deleted successfully": "商品が正常に削除されました",
"Product status updated to :status": "商品ステータスが :status に更新されました",
"Product updated successfully": "商品が正常に更新されました",
"Production Company": "製造会社",
"Profile": "プロフィール",
"Profile Information": "プロフィール情報",
"Profile Settings": "個人設定",
@@ -455,10 +514,14 @@
"Quick search...": "クイック検索...",
"Real-time OEE analysis awaits": "リアルタイム OEE 分析待機中",
"Real-time Operation Logs (Last 50)": "リアルタイム操作ログ (直近 50 件)",
"Real-time fleet efficiency and OEE metrics": "リアルタイムのフリート効率と OEE 指標",
"Real-time monitoring across all machines": "全機台のリアルタイム監視",
"Real-time performance analytics": "リアルタイム・パフォーマンス分析",
"Real-time status monitoring": "リアルタイムステータス監視",
"Receipt Printing": "レシート印刷",
"Recent Login": "最近のログイン",
"Regenerate": "再生成する",
"Regenerating the token will disconnect the physical machine until it is updated. Continue?": "トークンを再生成すると、更新されるまで物理マシンの接続が切断されます。続行しますか?",
"Remote Change": "リモートお釣り",
"Remote Checkout": "リモート決済",
"Remote Dispense": "リモート出庫",
@@ -471,6 +534,7 @@
"Replenishment Page": "補充画面",
"Replenishment Records": "補充記録",
"Replenishments": "補充",
"Reporting Period": "レポート期間",
"Reservation Members": "予約会員",
"Reservation System": "予約システム",
"Reservations": "予約",
@@ -488,13 +552,11 @@
"Role name already exists in this company.": "この会社には同じ名前のロールが既に存在します。",
"Role not found.": "ロールが見つかりませんでした。",
"Role updated successfully.": "ロールが正常に更新されました。",
"Points Rule": "ポイントルール",
"Points Settings": "ポイント設定",
"Points toggle": "ポイント切り替え",
"Roles": "ロール権限",
"Roles scoped to specific customer companies.": "適用於各個客戶單位的特定角色。",
"Roles scoped to specific customer companies.": "各顧客企業専用のロール。",
"Running Status": "稼働状況",
"SYSTEM": "システムレベル",
"Sale Price": "販売価格",
"Sales": "銷售管理",
"Sales Activity": "銷售活動",
"Sales Management": "販売管理",
@@ -507,28 +569,36 @@
"Saved.": "保存されました",
"Saving...": "儲存中...",
"Scale level and access control": "層級與存取控制",
"Scan this code to quickly access the maintenance form for this device.": "このコードをスキャンして、このデバイスのメンテナンスフォームに素早くアクセスしてください。",
"Search configurations...": "設定を検索...",
"Search customers...": "顧客を検索...",
"Search machines by name or serial...": "名称またはシリアル番号で検索...",
"Search machines...": "機台を検索...",
"Search machines...": "マシン名またはシリアル番号で検索...",
"Search models...": "型番を検索...",
"Search roles...": "ロールを検索...",
"Search serial no or name...": "シリアル番号または名前を検索...",
"Search serial or machine...": "シリアルまたはマシンを検索...",
"Search serial or name...": "シリアル番号または名称で検索...",
"Search users...": "ユーザーを検索...",
"Select All": "全選",
"Select Company": "会社を選択",
"Select Company": "会社を選択",
"Select Machine": "選擇機台",
"Select Machine to view metrics": "請選擇機台以查看指標",
"Select Model": "型番を選択",
"Select Owner": "所属会社を選択",
"Select Owner": "会社を選択",
"Select a machine to deep dive": "請選擇機台以開始深度分析",
"Select an asset from the left to start analysis": "分析を開始するには左側のデバイスを選択してください",
"Select date to sync data": "データ同期の日付を選択してください",
"Selected": "已選擇",
"Selected Date": "查詢日期",
"Selection": "選択済み",
"Serial & Version": "シリアルとバージョン",
"Serial NO": "シリアル番号",
"Serial No": "機台シリアル番号",
"Serial Number": "シリアル番号",
"Show": "表示",
"Show material code field in products": "商品情報に資材コードフィールドを表示する",
"Show points rules in products": "商品情報にポイントルール相關フィールドを表示する",
"Showing": "表示中",
"Showing :from to :to of :total items": ":total 件中 :from から :to 件を表示",
"Sign in to your account": "アカウントにサインイン",
@@ -539,7 +609,12 @@
"Slot Test": "テスト中",
"Some fields need attention": "一部のフィールドに注意が必要です",
"Special Permission": "特別権限",
"Specifications": "規格",
"Spring Channel Limit": "スプリング上限",
"Spring Limit": "スプリング上限",
"Staff Stock": "スタッフ在庫",
"Start Date": "開始日",
"Statistics": "統計データ",
"Status": "ステータス",
"Status / Temp / Sub / Card / Scan": "状態 / 温度 / 下位機 / カード / スキャン",
"Stock Management": "在庫管理",
@@ -557,6 +632,7 @@
"Super Admin": "スーパー管理者",
"Super-admin role cannot be assigned to tenant accounts.": "スーパー管理者ロールはテナントアカウントに割り当てることはできません。",
"Survey Analysis": "アンケート分析",
"Syncing Permissions...": "権限を同期中...",
"System Default": "系統預設",
"System Level": "システムレベル",
"System Official": "公式",
@@ -569,7 +645,6 @@
"Systems Initializing": "システム初期化中",
"TapPay Integration": "TapPay 統合決済",
"TapPay Integration Settings Description": "TapPay 決済連携設定",
"Statistics": "統計データ",
"Target": "ターゲット",
"Tax ID (Optional)": "納税者番号 (任意)",
"Temperature": "温度",
@@ -589,24 +664,30 @@
"Total Connected": "接続数合計",
"Total Customers": "顧客總數",
"Total Daily Sales": "本日累計銷量",
"Total Gross Value": "総売上額",
"Total Logins": "總ログイン數",
"Total Selected": "已選擇總數",
"Total Slots": "合計スロット数",
"Total items": "合計 :count 件",
"Track Channel Limit": "ベルトコンベア上限",
"Track Limit": "ベルトコンベア上限",
"Track device health and maintenance history": "デバイスの健全性とメンテナンス履歴を追跡します",
"Transfer Audit": "転送監査",
"Transfers": "転送",
"Tutorial Page": "チュートリアル画面",
"Type": "タイプ",
"UI Elements": "UI要素",
"Uncategorized": "未分類",
"Unified Operational Timeline": "整合式營運時序圖",
"Units": "ユニット",
"Unknown": "不明",
"Update": "更新",
"Update Authorization": "権限を更新",
"Update Customer": "顧客を更新",
"Update Password": "パスワードの更新",
"Update Product": "商品を更新",
"Update existing role and permissions.": "既存のロールと権限を更新します。",
"Update your account's profile information and email address.": "アカウントの氏名、電話番号、メールアドレスを更新します。",
"Validation Error": "検証エラー",
"Upload New Images": "新しい写真をアップロード",
"Uploading new images will replace all existing images.": "新しい写真をアップロードすると、既存のすべての写真が置き換えられます。",
"User": "一般ユーザー",
@@ -616,7 +697,9 @@
"Utilization Rate": "稼働率",
"Utilization Timeline": "稼動時序",
"Utilization, OEE and Operational Intelligence": "稼動率、OEE と運用インテリジェンス",
"Utilized Time": "稼働時間",
"Valid Until": "有効期限",
"Validation Error": "検証エラー",
"Vending Page": "販売画面",
"Venue Management": "会場管理",
"View Details": "詳細表示",
@@ -632,6 +715,7 @@
"Welcome Gift": "会員登録特典",
"Welcome Gift Status": "来店特典",
"Work Content": "作業内容",
"Yes, regenerate": "はい、再生成します",
"Yesterday": "昨日",
"You cannot assign permissions you do not possess.": "ご自身が所有していない権限を割り當てることはできません。",
"You cannot delete your own account.": "ご自身のアカウントは削除できません。",
@@ -669,6 +753,11 @@
"menu.basic.machines": "機台設定",
"menu.basic.payment-configs": "決済設定管理",
"menu.data-config": "データ設定",
"menu.data-config.admin-products": "商品ステータス",
"menu.data-config.advertisements": "広告管理",
"menu.data-config.badges": "バッジ設定",
"menu.data-config.points": "ポイント設定",
"menu.data-config.products": "商品管理",
"menu.data-config.sub-account-roles": "サブアカウントロール",
"menu.data-config.sub-accounts": "サブアカウント管理",
"menu.line": "LINE 設定",
@@ -687,6 +776,7 @@
"menu.sales": "売上レポート",
"menu.special-permission": "特殊権限",
"menu.warehouses": "倉庫管理",
"min": "分",
"of": "件中",
"permissions": "權限設定",
"permissions.accounts": "帳號管理",
@@ -695,6 +785,7 @@
"remote": "リモート管理",
"reservation": "予約システム",
"roles": "ロール權限",
"s": "秒",
"sales": "販売管理",
"special-permission": "特別権限",
"super-admin": "超級管理員",
@@ -703,76 +794,10 @@
"vs Yesterday": "前日比",
"warehouses": "倉庫管理",
"待填寫": "待填寫",
"Enabled": "有効中",
"Account :name status has been changed to :status.": "アカウント :name のステータスが :status に変更されました。",
"Cannot change Super Admin status.": "スーパー管理者のステータスは変更できません。",
"Confirm Status Change": "ステータス変更の確認",
"Disable Product Confirmation": "商品無効化の確認",
"Delete Product Confirmation": "商品削除の確認",
"Are you sure you want to change the status of this product? Disabled products will not be visible on the machine.": "この商品のステータスを変更してもよろしいですか?無効にされた商品はマシンに表示されません。",
"Are you sure you want to delete this product? All related historical translation data will also be removed.": "この商品を削除してもよろしいですか?関連するすべての履歴翻訳データも削除されます。",
"Are you sure you want to change the status? This may affect associated accounts.": "ステータスを変更してもよろしいですか?関連するアカウントに影響する可能性があります。",
"Confirm Account Status Change": "アカウントステータス変更の確認",
"Are you sure you want to change the status? After disabling, this account will no longer be able to log in to the system.": "ステータスを変更してもよろしいですか?無効化後、このアカウントはシステムにログインできなくなります。",
"Confirm Account Deactivation": "アカウント停止の確認",
"Are you sure you want to deactivate this account? After deactivating, this account will no longer be able to log in to the system.": "アカウントを停止してもよろしいですか?一旦停止するとシステムにログインできなくなります。",
"Enable": "有効にする",
"Disable": "無効にする",
"Regenerate": "再生成する",
"API Token Copied": "APIトークンがコピーされました",
"Yes, regenerate": "はい、再生成します",
"Regenerating the token will disconnect the physical machine until it is updated. Continue?": "トークンを再生成すると、更新されるまで物理マシンの接続が切断されます。続行しますか?",
"Error processing request": "リクエストの処理中にエラーが発生しました",
"API Token regenerated successfully.": "APIトークンが正常に再生成されました。",
"Product Name (Multilingual)": "商品名 (多言語)",
"Material Code": "物料コード",
"Full Points": "フルポイント",
"Half Points": "ハーフポイント",
"Half Points Amount": "ハーフポイント金額",
"Name in Traditional Chinese": "繁体字中国語名",
"Name in English": "英語名",
"Name in Japanese": "日本語名",
"Track Limit": "ベルトコンベア上限",
"Spring Limit": "スプリング上限",
"Manage your catalog, prices, and multilingual details.": "カタログ、価格、多言語詳細を管理します。",
"Product created successfully": "商品が正常に作成されました",
"Product updated successfully": "商品が正常に更新されました",
"Product deleted successfully": "商品が正常に削除されました",
"Fill in the product details below": "以下に商品の詳細を入力してください",
"Uncategorized": "未分類",
"Track Channel Limit": "ベルトコンベア上限",
"Spring Channel Limit": "スプリング上限",
"Product Details": "商品詳細",
"Production Company": "製造会社",
"Barcode": "バーコード",
"Specifications": "規格",
"Cost": "原価",
"Member Price": "会員価格",
"Sale Price": "販売価格",
"Update Product": "商品を更新",
"Edit Product": "商品を編集",
"Create Product": "商品を作成",
"Are you sure you want to delete this product?": "この商品を削除してもよろしいですか?",
"Feature Settings": "機能設定",
"Enable Material Code": "資材コードを有効化",
"Show material code field in products": "商品情報に資材コードフィールドを表示する",
"Enable Points": "ポイントルールを有効化",
"Show points rules in products": "商品情報にポイントルール相關フィールドを表示する",
"Product Image": "商品画像",
"PNG, JPG up to 2MB": "PNG, JPG (最大 2MB)",
"menu.data-config.products": "商品管理",
"menu.data-config.advertisements": "広告管理",
"menu.data-config.admin-products": "商品ステータス",
"menu.data-config.points": "ポイント設定",
"menu.data-config.badges": "バッジ設定",
"Create New Role": "新しいロールを作成",
"Create Sub Account Role": "サブアカウントロールを作成",
"Edit Sub Account Role": "サブアカウントロールを編集",
"New Role": "新しいロール",
"Manufacturer": "製造元",
"Product status updated to :status": "商品ステータスが :status に更新されました",
"Customer and associated accounts disabled successfully.": "顧客と関連アカウントが正常に無効化されました。",
"Customer enabled successfully.": "顧客が正常に有効化されました。",
"Cannot delete company with active accounts.": "有効なアカウントを持つ顧客を削除できません。",
"Customer deleted successfully.": "顧客が正常に削除されました。"
}
"Authorized Accounts Tab": "認定アカウント",
"Authorize Btn": "認可",
"Authorized Machines Management": "認定機台管理",
"Authorization updated successfully": "認証が更新されました",
"Authorized Status": "認可済み",
"Unauthorized Status": "未認可"
}

View File

@@ -2,6 +2,8 @@
"A new verification link has been sent to your email address.": "已將新的驗證連結發送至您的電子郵件地址。",
"AI Prediction": "AI智能預測",
"API Token": "API 金鑰",
"API Token Copied": "API 金鑰已複製",
"API Token regenerated successfully.": "API 金鑰重新產生成功。",
"APK Versions": "APK版本",
"APP Features": "APP功能",
"APP Management": "APP管理",
@@ -9,8 +11,10 @@
"APP_ID": "APP_ID",
"APP_KEY": "APP_KEY",
"Account": "帳號",
"Account :name status has been changed to :status.": "帳號 :name 的狀態已變更為 :status。",
"Account Info": "帳號資訊",
"Account Management": "帳號管理",
"Account Name": "帳號名",
"Account Name": "帳號名",
"Account Settings": "帳戶設定",
"Account Status": "帳號狀態",
"Account created successfully.": "帳號已成功建立。",
@@ -21,26 +25,28 @@
"Action": "操作",
"Actions": "操作",
"Active": "使用中",
"Active Status": "啟用狀態",
"Add Account": "新增帳號",
"Add Customer": "新增客戶",
"Add Machine": "新增機台",
"Add Machine Model": "新增機台型號",
"Add Maintenance Record": "新增維修管理單",
"Add Product": "新增商品",
"Add Role": "新增角色",
"Admin": "管理員",
"Admin Name": "管理員姓名",
"Admin Name": "名",
"Admin Page": "管理頁",
"Admin Sellable Products": "管理者可賣",
"Admin display name": "管理員顯示名稱",
"Administrator": "管理員",
"Advertisement Management": "廣告管理",
"Affiliated Unit": "所屬單位",
"Affiliated Unit": "公司名稱",
"Affiliation": "所屬單位",
"Alert Summary": "告警摘要",
"Alerts": "中心告警",
"Alerts Pending": "待處理告警",
"All": "全部",
"All Affiliations": "全部單位",
"All Affiliations": "所有公司",
"All Categories": "所有分類",
"All Companies": "所有公司",
"All Levels": "所有層級",
@@ -51,11 +57,17 @@
"Analysis Permissions": "分析管理權限",
"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.": "確定要變更此商品的狀態嗎?停用的商品將不會在機台上顯示。",
"Are you sure you want to change the status? After disabling, this account will no longer be able to log in to the system.": "您確定要變更狀態嗎?停用之後,該帳號將會立即被登出且無法再登入系統。",
"Are you sure you want to change the status? This may affect associated accounts.": "您確定要變更狀態嗎?這可能會影響相關帳號的權限效力。",
"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 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.": "確定要刪除此項目嗎?此操作無法復原。",
"Are you sure you want to delete this product?": "您確定要刪除此商品嗎?",
"Are you sure you want to delete this product? All related historical translation data will also be removed.": "確定要刪除此商品嗎?所有相關的歷史翻譯數據也將被移除。",
"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.": "您確定要繼續嗎?此操作將無法復原。",
@@ -65,20 +77,29 @@
"Assigned Machines": "授權機台",
"Audit Management": "稽核管理",
"Audit Permissions": "稽核管理權限",
"Authorize": "授權",
"Authorized Accounts": "授權帳號",
"Authorized Machines": "授權機台",
"Authorized Machines Management": "授權機台管理",
"Availability": "可用性 (Availability)",
"Available Machines": "可供分配的機台",
"Avatar updated successfully.": "頭像已成功更新。",
"Avg Cycle": "平均週期",
"Badge Settings": "識別證",
"Barcode": "條碼",
"Basic Information": "基本資訊",
"Basic Settings": "基本設定",
"Basic Specifications": "基本規格",
"Batch No": "批號",
"Belongs To": "所屬單位",
"Belongs To Company": "所屬單位",
"Belongs To": "公司名稱",
"Belongs To Company": "公司名稱",
"Business Type": "業務類型",
"Buyout": "買斷",
"Cancel": "取消",
"Cancel Purchase": "取消購買",
"Cannot Delete Role": "無法刪除該角色",
"Cannot change Super Admin status.": "無法變更超級管理員的狀態。",
"Cannot delete company with active accounts.": "無法刪除仍有客用帳號的客戶。",
"Cannot delete model that is currently in use by machines.": "無法刪除目前正在被機台使用的型號。",
"Cannot delete role with active users.": "無法刪除已有綁定帳號的角色。",
"Card Reader": "刷卡機",
@@ -88,6 +109,9 @@
"Category": "類別",
"Change": "更換",
"Change Stock": "零錢庫存",
"Channel Limits": "貨道上限",
"Channel Limits (Track/Spring)": "貨道上限 (履帶/彈簧)",
"Channel Limits Configuration": "貨道上限配置",
"ChannelId": "ChannelId",
"ChannelSecret": "ChannelSecret",
"Checkout Time 1": "卡機結帳時間1",
@@ -105,32 +129,50 @@
"Config Name": "配置名稱",
"Configuration Name": "配置名稱",
"Confirm": "確認",
"Confirm Account Deactivation": "停用帳號確認",
"Confirm Account Status Change": "帳號狀態變更確認",
"Confirm Deletion": "確認刪除資料",
"Confirm Password": "確認新密碼",
"Confirm Status Change": "確認變更狀態",
"Connecting...": "連線中",
"Connectivity Status": "連線狀態概況",
"Connectivity vs Sales Correlation": "連線狀態與銷售關聯分析",
"Contact Info": "聯絡資訊",
"Contact & Details": "聯絡資訊與詳情",
"Contact Email": "聯絡人信箱",
"Contact Info": "聯絡資訊",
"Contact Name": "聯絡人姓名",
"Contact Phone": "聯絡人電話",
"Contract Period": "合約期間",
"Contract Until (Optional)": "合約到期日 (選填)",
"Cost": "成本",
"Coupons": "優惠券",
"Create": "建立",
"Create Config": "建立配置",
"Create Machine": "新增機台",
"Create New Role": "建立新角色",
"Create Payment Config": "新增金流配置",
"Create Product": "建立商品",
"Create Role": "建立角色",
"Create Sub Account Role": "建立子帳號角色",
"Create a new role and assign permissions.": "建立新角色並分配對應權限。",
"Critical": "嚴重",
"Current": "當前",
"Current Password": "當前密碼",
"Current Status": "當前狀態",
"Current Stock": "當前庫存",
"Current Type": "當前類型",
"Current:": "現:",
"Customer Details": "客戶詳情",
"Customer Info": "客戶資訊",
"Customer Management": "客戶管理",
"Customer Payment Config": "客戶金流設定",
"Customer created successfully.": "客戶新增成功",
"Customer updated successfully.": "客戶新成功",
"Customer and associated accounts disabled successfully.": "客戶及其關聯帳號已成功停用。",
"Customer created successfully.": "客戶新成功",
"Customer deleted successfully.": "客戶已成功刪除。",
"Customer enabled successfully.": "客戶已成功啟用。",
"Customer updated successfully.": "客戶更新成功。",
"Cycle Efficiency": "週期效率",
"Daily Revenue": "當日營收",
"Danger Zone: Delete Account": "危險區域:刪除帳號",
"Dashboard": "儀表板",
"Data Configuration": "資料設定",
@@ -140,16 +182,20 @@
"Default Not Donate": "預設不捐贈",
"Define and manage security roles and permissions.": "定義並管理系統安全角色與權限。",
"Define new third-party payment parameters": "定義新的第三方支付參數",
"Feature Toggles": "功能開關",
"Delete": "刪除",
"Delete Account": "刪除帳號",
"Delete Permanently": "確認永久刪除資料",
"Delete Product": "刪除商品",
"Delete Product Confirmation": "刪除商品確認",
"Deposit Bonus": "儲值回饋",
"Describe the repair or maintenance status...": "請描述維修或保養狀況...",
"Deselect All": "取消全選",
"Detail": "詳細",
"Device Information": "設備資訊",
"Device Status Logs": "設備狀態紀錄",
"Devices": "台設備",
"Disable": "停用",
"Disable Product Confirmation": "停用商品確認",
"Disabled": "已停用",
"Discord Notifications": "Discord通知",
"Dispense Failed": "出貨失敗",
@@ -168,12 +214,20 @@
"Edit Machine Model": "編輯機台型號",
"Edit Machine Settings": "編輯機台設定",
"Edit Payment Config": "編輯金流配置",
"Edit Product": "編輯商品",
"Edit Role": "編輯角色",
"Edit Role Permissions": "編輯角色權限",
"Edit Settings": "編輯設定",
"Edit Sub Account Role": "編輯子帳號角色",
"Email": "電子郵件",
"Enable": "啟用",
"Enable Material Code": "啟用物料編號",
"Enable Points": "啟用點數規則",
"Enabled": "已啟用",
"Enabled/Disabled": "啟用/停用",
"End Date": "截止日",
"Engineer": "維修人員",
"English": "英文",
"Ensure your account is using a long, random password to stay secure.": "確保您的帳號使用了足夠強度的隨機密碼以維持安全。",
"Enter login ID": "請輸入登入帳號",
"Enter machine location": "請輸入機台地點",
@@ -183,30 +237,36 @@
"Enter serial number": "請輸入機台序號",
"Enter your password to confirm": "請輸入您的密碼以確認",
"Equipment efficiency and OEE metrics": "設備效能與 OEE 綜合指標",
"Avg Cycle": "平均週期",
"Cycle Efficiency": "週期效率",
"Error": "異常",
"Error processing request": "處理請求時發生錯誤",
"Execution Time": "執行時間",
"Expired": "已過期",
"Expired / Disabled": "已過期 / 停用",
"Expiry Date": "有效日期",
"Expiry Management": "效期管理",
"Failed to fetch machine data.": "無法取得機台資料。",
"Failed to load permissions": "載入權限失敗",
"Failed to save permissions.": "無法儲存權限設定。",
"Failed to update machine images: ": "更新機台圖片失敗:",
"Feature Settings": "功能設定",
"Feature Toggles": "功能開關",
"Fill in the device repair or maintenance details": "填寫設備維修或保養詳情",
"Fill in the product details below": "請在下方填寫商品的詳細資訊",
"Firmware Version": "韌體版本",
"Fleet Avg OEE": "全機台平均 OEE",
"Daily Revenue": "當日營收",
"Fleet Performance": "全機台效能",
"From": "從",
"From:": "起:",
"Full Access": "全機台授權",
"Full Name": "名",
"Full Name": "名",
"Full Points": "全點數",
"Games": "互動遊戲",
"General permissions not linked to a specific menu.": "未連結到特定選單的一般權限。",
"Gift Definitions": "禮品設定",
"Global roles accessible by all administrators.": "適用於所有管理者的全域角色。",
"Got it": "知道了",
"Half Points": "半點數",
"Half Points Amount": "半點數金額",
"Hardware & Network": "硬體與網路",
"Hardware & Slots": "硬體與貨道",
"HashIV": "HashIV",
@@ -215,9 +275,9 @@
"Heating End Time": "關閉-加熱時間",
"Heating Range": "加熱時段",
"Heating Start Time": "開啟-加熱時間",
"Channel Limits": "貨道上限",
"Helper": "小幫手",
"Home Page": "主頁面",
"Identity & Codes": "識別與代碼",
"Info": "一般",
"Initial Admin Account": "初始管理帳號",
"Initial Role": "初始角色",
@@ -225,10 +285,10 @@
"Invoice Status": "發票開立狀態",
"Items": "個項目",
"JKO_MERCHANT_ID": "街口支付 商店代號",
"Japanese": "日文",
"Joined": "加入日期",
"Key": "金鑰 (Key)",
"Key No": "鑰匙編號",
"Identity & Codes": "識別與代碼",
"LEVEL TYPE": "層級類型",
"LINE Pay Direct": "LINE Pay 官方直連",
"LINE Pay Direct Settings Description": "LINE Pay 官方直連設定",
@@ -239,6 +299,7 @@
"Last Signal": "最後訊號時間",
"Last Time": "最後時間",
"Last Updated": "最後更新日期",
"Lease": "租賃",
"Level": "層級",
"Line Coupons": "Line優惠券",
"Line Machines": "Line機台",
@@ -248,14 +309,15 @@
"Line Orders": "Line訂單",
"Line Permissions": "Line 管理權限",
"Line Products": "Line商品",
"Live Fleet Updates": "即時數據更新",
"Loading machines...": "正在載入機台...",
"Loyalty & Features": "行銷與點數",
"Loading...": "載入中...",
"Location": "位置",
"Locked Page": "鎖定頁",
"Login History": "登入歷史",
"Logout": "登出",
"Logs": "日誌",
"Loyalty & Features": "行銷與點數",
"Machine Count": "機台數量",
"Machine Details": "機台詳情",
"Machine Images": "機台照片",
@@ -270,12 +332,14 @@
"Machine Model Settings": "機台型號設定",
"Machine Name": "機台名稱",
"Machine Permissions": "授權機台",
"Machine Registry": "機台清冊",
"Machine Reports": "機台報表",
"Machine Restart": "機台重啟",
"Machine Settings": "機台設定",
"Machine Status": "機台狀態",
"Machine Status List": "機台運行狀態列表",
"Machine Stock": "機台庫存",
"Machine Utilization": "機台稼動率",
"Machine created successfully.": "機台已成功建立。",
"Machine images updated successfully.": "機台圖片已成功更新。",
"Machine model created successfully.": "機台型號已成功建立。",
@@ -284,8 +348,6 @@
"Machine settings updated successfully.": "機台設定已成功更新。",
"Machines": "機台列表",
"Machines Online": "在線機台數",
"Machine Utilization": "機台稼動率",
"Machine Registry": "機台清冊",
"Maintenance": "保養",
"Maintenance Content": "維修內容",
"Maintenance Date": "維修日期",
@@ -294,21 +356,24 @@
"Maintenance QR": "維修掃描碼",
"Maintenance QR Code": "維修掃描碼",
"Maintenance Records": "維修管理單",
"Live Fleet Updates": "即時數據更新",
"Maintenance record created successfully": "維修紀錄已成功建立",
"Scan this code to quickly access the maintenance form for this device.": "掃描此 QR Code 即可快速進入此設備的維修單填寫頁面。",
"Manage Account Access": "管理帳號存取",
"Manage Expiry": "進入效期管理",
"Manage administrative and tenant accounts": "管理系統管理者與租戶帳號",
"Manage all tenant accounts and validity": "管理所有租戶帳號與合約效期",
"Manage your catalog, prices, and multilingual details.": "管理您的商品型錄、價格及多語系詳情。",
"Manage your machine fleet and operational data": "管理您的機台群組與營運數據",
"Manage your profile information, security settings, and login history": "管理您的個人資訊、安全設定與登入紀錄",
"Management of operational parameters": "機台運作參數管理",
"Management of operational parameters and models": "管理運作參數與型號",
"Manufacturer": "製造商",
"Material Code": "物料代碼",
"Max 3": "最多 3 張",
"Member": "會員價",
"Member & External": "會員與外部系統",
"Member List": "會員列表",
"Member Management": "會員管理",
"Member Price": "會員價",
"Member System": "會員系統",
"Membership Tiers": "會員等級",
"Menu Permissions": "選單權限",
@@ -317,7 +382,6 @@
"Message": "訊息",
"Message Content": "日誌內容",
"Message Display": "訊息顯示",
"min": "分",
"Min 8 characters": "至少 8 個字元",
"Model": "機台型號",
"Model Name": "型號名稱",
@@ -326,15 +390,20 @@
"Monitor events and system activity across your vending fleet.": "跨機台連線動態與系統日誌監控。",
"Monthly Transactions": "本月交易統計",
"Monthly cumulative revenue overview": "本月累計營收概況",
"Name": "名",
"Name": "名",
"Name in English": "英文名稱",
"Name in Japanese": "日文名稱",
"Name in Traditional Chinese": "繁體中文名稱",
"Never Connected": "從未連線",
"New Password": "新密碼",
"New Password (leave blank to keep current)": "新密碼 (若不修改請留空)",
"New Record": "新增單據",
"New Role": "新角色",
"New Sub Account Role": "新增子帳號角色",
"No Company": "系統",
"Next": "下一頁",
"No Company": "系統",
"No Invoice": "不開立發票",
"No Machine Selected": "尚未選擇機台",
"No accounts found": "找不到帳號資料",
"No alert summary": "暫無告警記錄",
"No configurations found": "暫無相關配置",
@@ -350,9 +419,8 @@
"No machines available": "目前沒有可供分配的機台",
"No machines available in this company.": "此客戶目前沒有可供分配的機台。",
"No maintenance records found": "找不到維修紀錄",
"No Machine Selected": "尚未選擇機台",
"No matching machines": "找不到符合的機台",
"No matching logs found": "找不到符合條件的日誌",
"No matching machines": "找不到符合的機台",
"No permissions": "無權限項目",
"No roles found.": "找不到角色資料。",
"No slots found": "未找到貨道資訊",
@@ -362,11 +430,8 @@
"Not Used": "不使用",
"Not Used Description": "不使用第三方支付介接",
"Notes": "備註",
"OEE Score": "OEE 綜合得分",
"OEE Efficiency Trend": "OEE 效率趨勢",
"Online Status": "在線狀態",
"Optimized Performance": "效能優化",
"Output Count": "出貨數量",
"OEE Score": "OEE 綜合得分",
"OEE.Activity": "營運活動",
"OEE.Errors": "異常",
"OEE.Hours": "小時",
@@ -379,21 +444,28 @@
"Online": "線上",
"Online Duration": "累積連線時數",
"Online Machines": "在線機台",
"Online Status": "在線狀態",
"Only system roles can be assigned to platform administrative accounts.": "僅系統角色可指派給平台管理帳號。",
"Operational Parameters": "運作參數",
"Operations": "運作設定",
"Optimal": "良好",
"Optimized Performance": "效能優化",
"Optimized for display. Supported formats: JPG, PNG, WebP.": "已針對顯示進行優化。支援格式JPG, PNG, WebP。",
"Optional": "選填",
"Order Management": "訂單管理",
"Orders": "購買單",
"Original": "原始",
"Original Type": "原始類型",
"Original:": "原:",
"Other Permissions": "其他權限",
"Others": "其他功能",
"Owner": "所屬單位",
"Output Count": "出貨數量",
"Owner": "公司名稱",
"PARTNER_KEY": "PARTNER_KEY",
"PI_MERCHANT_ID": "Pi 拍錢包 商店代號",
"PS_MERCHANT_ID": "全盈+Pay 商店代號",
"PNG, JPG up to 2MB": "支援 PNG, JPG (最大 2MB)",
"PS_LEVEL": "PS_LEVEL",
"PS_MERCHANT_ID": "全盈+Pay 商店代號",
"Parameters": "參數設定",
"Pass Code": "通行碼",
"Pass Codes": "通行碼",
@@ -408,12 +480,12 @@
"Payment Configuration updated successfully.": "金流設定已成功更新。",
"Payment Selection": "付款選擇",
"Pending": "待核效期",
"Pricing Information": "價格資訊",
"Performance": "效能 (Performance)",
"Permanent": "永久授權",
"Permanently Delete Account": "永久刪除帳號",
"Permission Settings": "權限設定",
"Permissions": "權限",
"Permissions updated successfully": "授權更新成功",
"Phone": "手機號碼",
"Photo Slot": "照片欄位",
"Pickup Code": "取貨碼",
@@ -423,10 +495,24 @@
"Please select a machine to view metrics": "請選擇機台以查看數據",
"Point Rules": "點數規則",
"Point Settings": "點數設定",
"Points Rule": "點數規則",
"Points Settings": "點數設定",
"Points toggle": "點數開關",
"Previous": "上一頁",
"Price / Member": "售價 / 會員價",
"Pricing Information": "價格資訊",
"Product Details": "商品詳情",
"Product Image": "商品圖片",
"Product Info": "商品資訊",
"Product Management": "商品管理",
"Product Name (Multilingual)": "商品名稱 (多語系)",
"Product Reports": "商品報表",
"Product Status": "商品狀態",
"Product created successfully": "商品已成功建立",
"Product deleted successfully": "商品已成功刪除",
"Product status updated to :status": "商品狀態已更新為 :status",
"Product updated successfully": "商品已成功更新",
"Production Company": "生產公司",
"Profile": "個人檔案",
"Profile Information": "個人基本資料",
"Profile Settings": "個人設定",
@@ -443,14 +529,15 @@
"Quick Select": "快速選取",
"Quick search...": "快速搜尋...",
"Real-time OEE analysis awaits": "即時 OEE 分析預備中",
"Real-time fleet efficiency and OEE metrics": "全機台即時效率與 OEE 指標",
"Real-time performance analytics": "即時效能分析",
"Reporting Period": "報表區間",
"Real-time Operation Logs (Last 50)": "即時操作日誌 (最後 50 筆)",
"Real-time fleet efficiency and OEE metrics": "全機台即時效率與 OEE 指標",
"Real-time monitoring across all machines": "跨機台即時狀態監控",
"Real-time performance analytics": "即時效能分析",
"Real-time status monitoring": "即時監控機台連線動態",
"Receipt Printing": "收據簽單",
"Recent Login": "最近登入",
"Regenerate": "重新產生",
"Regenerating the token will disconnect the physical machine until it is updated. Continue?": "重新產生金鑰將導致實體機台暫時失去連線,必須於機台端更新此新金鑰才能恢復。確定繼續嗎?",
"Remote Change": "遠端找零",
"Remote Checkout": "遠端結帳",
"Remote Dispense": "遠端出貨",
@@ -463,9 +550,11 @@
"Replenishment Page": "補貨頁",
"Replenishment Records": "機台補貨紀錄",
"Replenishments": "機台補貨單",
"Reporting Period": "報表區間",
"Reservation Members": "預約會員",
"Reservation System": "預約系統",
"Reservations": "預約管理",
"Retail Price": "零售價",
"Returns": "回庫單",
"Risk": "風險狀態",
"Role": "角色",
@@ -480,14 +569,11 @@
"Role name already exists in this company.": "該公司已存在相同名稱的角色。",
"Role not found.": "角色不存在。",
"Role updated successfully.": "角色已成功更新。",
"Points Rule": "點數規則",
"Points Settings": "點數設定",
"Points toggle": "點數開關",
"Roles": "角色權限",
"Roles scoped to specific customer companies.": "適用於各個客戶單位的特定角色。",
"Roles scoped to specific customer companies.": "適用於各個客戶公司的特定角色。",
"Running Status": "運行狀態",
"s": "秒",
"SYSTEM": "系統層級",
"Sale Price": "售價",
"Sales": "銷售管理",
"Sales Activity": "銷售活動",
"Sales Management": "銷售管理",
@@ -500,29 +586,36 @@
"Saved.": "已儲存",
"Saving...": "儲存中...",
"Scale level and access control": "層級與存取控制",
"Scan this code to quickly access the maintenance form for this device.": "掃描此 QR Code 即可快速進入此設備的維修單填寫頁面。",
"Search accounts...": "搜尋帳號...",
"Search configurations...": "搜尋設定...",
"Search customers...": "搜尋客戶...",
"Search machines by name or serial...": "搜尋機台名稱或序號...",
"Search machines...": "搜尋機台...",
"Search machines...": "搜尋機台名稱或序號...",
"Search models...": "搜尋型號...",
"Search products...": "搜尋商品...",
"Search roles...": "搜尋角色...",
"Search serial no or name...": "搜尋序號或機台名稱...",
"Search serial or machine...": "搜尋序號或機台名稱...",
"Search users...": "搜尋用戶...",
"Select All": "全選",
"Select Company": "選擇所屬單位",
"Select Category": "選擇類別",
"Select Company": "選擇公司名稱",
"Select Machine": "選擇機台",
"Select Machine to view metrics": "請選擇機台以查看指標",
"Select Model": "選擇型號",
"Select Owner": "選擇所屬單位",
"Select Owner": "選擇公司名稱",
"Select a machine to deep dive": "請選擇機台以開始深度分析",
"Select date to sync data": "選擇日期以同步數據",
"Selected": "已選擇",
"Selected Date": "查詢日期",
"Selection": "已選擇",
"Serial & Version": "序號與版本",
"Serial No": "機台序號",
"Serial Number": "機台序號",
"Show": "顯示",
"Show material code field in products": "在商品資料中顯示物料編號欄位",
"Show points rules in products": "在商品資料中顯示點數規則相關欄位",
"Showing": "顯示第",
"Showing :from to :to of :total items": "顯示第 :from 到 :to 項,共 :total 項",
"Sign in to your account": "隨時隨地掌控您的業務。",
@@ -533,7 +626,13 @@
"Slot Test": "貨道測試",
"Some fields need attention": "部分欄位需要注意",
"Special Permission": "特殊權限",
"Specification": "規格",
"Specifications": "規格",
"Spring Channel Limit": "彈簧貨道上限",
"Spring Limit": "彈簧貨道上限",
"Staff Stock": "人員庫存",
"Start Date": "起始日",
"Statistics": "數據統計",
"Status": "狀態",
"Status / Temp / Sub / Card / Scan": "狀態 / 溫度 / 下位機 / 刷卡機 / 掃碼機",
"Stock Management": "庫存管理單",
@@ -551,6 +650,8 @@
"Super Admin": "超級管理員",
"Super-admin role cannot be assigned to tenant accounts.": "超級管理員角色無法指派給租戶帳號。",
"Survey Analysis": "問卷分析",
"Syncing Permissions...": "正在同步權限...",
"System": "系統",
"System Default": "系統預設",
"System Level": "系統層級",
"System Official": "系統層",
@@ -561,10 +662,8 @@
"System super admin accounts cannot be deleted.": "系統超級管理員帳號無法刪除。",
"System super admin accounts cannot be modified via this interface.": "系統超級管理員帳號無法透過此介面修改。",
"Systems Initializing": "系統初始化中",
"System": "系統",
"TapPay Integration": "TapPay 支付串接",
"TapPay Integration Settings Description": "喬睿科技支付串接設定",
"Statistics": "數據統計",
"Target": "目標",
"Tax ID": "統一編號",
"Tax ID (Optional)": "統一編號 (選填)",
@@ -580,30 +679,37 @@
"Timer": "計時器",
"Timestamp": "時間戳記",
"To": "至",
"To:": "終:",
"Today Cumulative Sales": "今日累積銷售",
"Today's Transactions": "今日交易額",
"Total Connected": "總計連線數",
"Total Customers": "客戶總數",
"Total Daily Sales": "本日累計銷量",
"Total Gross Value": "銷售總結",
"Total Logins": "總登入次數",
"Total Selected": "已選擇總數",
"Total Slots": "總貨道數",
"Total items": "總計 :count 項",
"Track Channel Limit": "履帶貨道上限",
"Track Limit": "履帶貨道上限",
"Track device health and maintenance history": "追蹤設備健康與維修歷史",
"Total Gross Value": "銷售總結",
"Traditional Chinese": "繁體中文",
"Transfer Audit": "調撥單",
"Transfers": "調撥單",
"Tutorial Page": "教學頁",
"Type": "類型",
"UI Elements": "UI元素",
"Uncategorized": "未分類",
"Unified Operational Timeline": "整合式營運時序圖",
"Units": "台",
"Unknown": "未知",
"Update": "更新",
"Update Authorization": "更新授權",
"Update Customer": "更新客戶",
"Update Password": "更改密碼",
"Update Product": "更新商品",
"Update existing role and permissions.": "更新現有角色與權限設定。",
"Update your account's profile information and email address.": "更新您的帳號名、手機號碼與電子郵件地址。",
"Validation Error": "驗證錯誤",
"Update your account's profile information and email address.": "更新您的帳號名、手機號碼與電子郵件地址。",
"Upload New Images": "上傳新照片",
"Uploading new images will replace all existing images.": "上傳新照片將會取代所有現有照片。",
"User": "一般用戶",
@@ -611,11 +717,11 @@
"Username": "使用者帳號",
"Users": "帳號數",
"Utilization Rate": "機台稼動率",
"Utilized Time": "稼動持續時間",
"Units": "台",
"Utilization Timeline": "稼動時序",
"Utilization, OEE and Operational Intelligence": "稼動率、OEE 與營運情報",
"Utilized Time": "稼動持續時間",
"Valid Until": "合約到期日",
"Validation Error": "驗證錯誤",
"Vending Page": "販賣頁",
"Venue Management": "場地管理",
"View Details": "查看詳情",
@@ -631,6 +737,7 @@
"Welcome Gift": "註冊成效禮",
"Welcome Gift Status": "來店禮",
"Work Content": "工作內容",
"Yes, regenerate": "確認重新產生",
"Yesterday": "昨日",
"You cannot assign permissions you do not possess.": "您無法指派您自身不具備的權限。",
"You cannot delete your own account.": "您無法刪除自己的帳號。",
@@ -648,6 +755,7 @@
"data-config": "資料設定",
"data-config.sub-account-roles": "子帳號角色",
"data-config.sub-accounts": "子帳號管理",
"e.g. 500ml / 300g": "例如500ml / 300g",
"e.g. John Doe": "例如:張曉明",
"e.g. TWSTAR": "例如TWSTAR",
"e.g. Taiwan Star": "例如:台灣之星",
@@ -668,6 +776,11 @@
"menu.basic.machines": "機台設定",
"menu.basic.payment-configs": "客戶金流設定",
"menu.data-config": "資料設定",
"menu.data-config.admin-products": "商品狀態",
"menu.data-config.advertisements": "廣告管理",
"menu.data-config.badges": "徽章設定",
"menu.data-config.points": "點數設定",
"menu.data-config.products": "商品管理",
"menu.data-config.sub-account-roles": "子帳號角色",
"menu.data-config.sub-accounts": "子帳號管理",
"menu.line": "LINE 配置",
@@ -686,6 +799,7 @@
"menu.sales": "銷售報表",
"menu.special-permission": "特殊權限",
"menu.warehouses": "倉儲管理",
"min": "分",
"of": "總計",
"permissions": "權限設定",
"permissions.accounts": "帳號管理",
@@ -694,6 +808,7 @@
"remote": "遠端管理",
"reservation": "預約系統",
"roles": "角色權限",
"s": "秒",
"sales": "銷售管理",
"special-permission": "特殊權限",
"super-admin": "超級管理員",
@@ -702,94 +817,9 @@
"vs Yesterday": "較昨日",
"warehouses": "倉庫管理",
"待填寫": "待填寫",
"Enabled": "已啟用",
"Account :name status has been changed to :status.": "帳號 :name 的狀態已變更為 :status。",
"Cannot change Super Admin status.": "無法變更超級管理員的狀態。",
"Confirm Status Change": "確認變更狀態",
"Disable Product Confirmation": "停用商品確認",
"Delete Product Confirmation": "刪除商品確認",
"Are you sure you want to change the status of this product? Disabled products will not be visible on the machine.": "確定要變更此商品的狀態嗎?停用的商品將不會在機台上顯示。",
"Are you sure you want to delete this product? All related historical translation data will also be removed.": "確定要刪除此商品嗎?所有相關的歷史翻譯數據也將被移除。",
"Are you sure you want to change the status? This may affect associated accounts.": "您確定要變更狀態嗎?這可能會影響相關帳號的權限效力。",
"Confirm Account Status Change": "帳號狀態變更確認",
"Are you sure you want to change the status? After disabling, this account will no longer be able to log in to the system.": "您確定要變更狀態嗎?停用之後,該帳號將會立即被登出且無法再登入系統。",
"Confirm Account Deactivation": "停用帳號確認",
"Are you sure you want to deactivate this account? After deactivating, this account will no longer be able to log in to the system.": "確定要停用此帳號嗎?停用後將無法登入系統。",
"Enable": "啟用",
"Disable": "停用",
"Regenerate": "重新產生",
"API Token Copied": "API 金鑰已複製",
"Yes, regenerate": "確認重新產生",
"Regenerating the token will disconnect the physical machine until it is updated. Continue?": "重新產生金鑰將導致實體機台暫時失去連線,必須於機台端更新此新金鑰才能恢復。確定繼續嗎?",
"Error processing request": "處理請求時發生錯誤",
"API Token regenerated successfully.": "API 金鑰重新產生成功。",
"Product Name (Multilingual)": "商品名稱 (多語系)",
"Material Code": "物料代碼",
"Full Points": "全點數",
"Half Points": "半點數",
"Half Points Amount": "半點數金額",
"Name in Traditional Chinese": "繁體中文名稱",
"Name in English": "英文名稱",
"Name in Japanese": "日文名稱",
"Track Limit": "履帶貨道上限",
"Spring Limit": "彈簧貨道上限",
"Channel Limits Configuration": "貨道上限配置",
"Manage your catalog, prices, and multilingual details.": "管理您的商品型錄、價格及多語系詳情。",
"Product created successfully": "商品已成功建立",
"Product updated successfully": "商品已成功更新",
"Product deleted successfully": "商品已成功刪除",
"Fill in the product details below": "請在下方填寫商品的詳細資訊",
"Uncategorized": "未分類",
"Track Channel Limit": "履帶貨道上限",
"Spring Channel Limit": "彈簧貨道上限",
"Product Details": "商品詳情",
"Production Company": "生產公司",
"Barcode": "條碼",
"Specifications": "規格",
"Cost": "成本",
"Member Price": "會員價",
"Retail Price": "零售價",
"Sale Price": "售價",
"Update Product": "更新商品",
"Edit Product": "編輯商品",
"Create Product": "建立商品",
"Are you sure you want to delete this product?": "您確定要刪除此商品嗎?",
"Channel Limits (Track/Spring)": "貨道上限 (履帶/彈簧)",
"Member": "會員價",
"Search products...": "搜尋商品...",
"Product Info": "商品資訊",
"Price / Member": "售價 / 會員價",
"Add Product": "新增商品",
"Delete Product": "刪除商品",
"Specification": "規格",
"e.g. 500ml / 300g": "例如500ml / 300g",
"Active Status": "啟用狀態",
"Traditional Chinese": "繁體中文",
"English": "英文",
"Japanese": "日文",
"Select Category": "選擇類別",
"Feature Settings": "功能設定",
"Enable Material Code": "啟用物料編號",
"Show material code field in products": "在商品資料中顯示物料編號欄位",
"Enable Points": "啟用點數規則",
"Show points rules in products": "在商品資料中顯示點數規則相關欄位",
"Customer Details": "客戶詳情",
"Current Status": "當前狀態",
"Product Image": "商品圖片",
"PNG, JPG up to 2MB": "支援 PNG, JPG (最大 2MB)",
"menu.data-config.products": "商品管理",
"menu.data-config.advertisements": "廣告管理",
"menu.data-config.admin-products": "商品狀態",
"menu.data-config.points": "點數設定",
"menu.data-config.badges": "徽章設定",
"Create New Role": "建立新角色",
"Create Sub Account Role": "建立子帳號角色",
"Edit Sub Account Role": "編輯子帳號角色",
"New Role": "新角色",
"Manufacturer": "製造商",
"Product status updated to :status": "商品狀態已更新為 :status",
"Customer and associated accounts disabled successfully.": "客戶及其關聯帳號已成功停用。",
"Customer enabled successfully.": "客戶已成功啟用。",
"Cannot delete company with active accounts.": "無法刪除仍有客用帳號的客戶。",
"Customer deleted successfully.": "客戶已成功刪除。"
}
"Authorized Accounts Tab": "授權帳號",
"Authorize Btn": "授權",
"Authorization updated successfully": "授權更新成功",
"Authorized Status": "已授權",
"Unauthorized Status": "未授權"
}

View File

@@ -289,7 +289,6 @@
.hs-select-menu {
@apply mt-2 max-h-72 p-2 space-y-0.5 z-50 bg-white border border-slate-200 rounded-2xl shadow-2xl overflow-y-auto;
@apply dark:bg-slate-900 dark:border-slate-800;
backdrop-filter: blur(12px);
}
.hs-select-option {

View File

@@ -19,6 +19,7 @@
showMaintenanceQrModal: false,
maintenanceQrMachineName: '',
maintenanceQrUrl: '',
permissionSearchQuery: '',
openMaintenanceQr(machine) {
this.maintenanceQrMachineName = machine.name;
const baseUrl = '{{ route('admin.maintenance.create', ['serial_no' => 'SERIAL_NO']) }}';
@@ -112,6 +113,71 @@
this.loadingRegenerate = false;
window.dispatchEvent(new CustomEvent('toast', { detail: { message: '{{ __('Error processing request') }}', type: 'error' } }));
});
},
// Machine Permissions (Migrated from Account Management)
showPermissionModal: false,
isPermissionsLoading: false,
targetUserId: null,
targetUserName: '',
allMachines: [],
allMachinesCount: 0,
permissions: {},
openPermissionModal(user) {
this.targetUserId = user.id;
this.targetUserName = user.name;
this.showPermissionModal = true;
this.isPermissionsLoading = true;
this.permissions = {};
this.allMachines = [];
this.permissionSearchQuery = '';
fetch(`/admin/basic-settings/machines/permissions/accounts/${user.id}`)
.then(res => res.json())
.then(data => {
if (data.machines) {
this.allMachines = data.machines;
this.allMachinesCount = data.machines.length;
const tempPermissions = {};
data.machines.forEach(m => {
tempPermissions[m.id] = (data.assigned_ids || []).includes(m.id);
});
this.permissions = tempPermissions;
}
})
.catch(e => {
window.dispatchEvent(new CustomEvent('toast', { detail: { message: '{{ __('Failed to load permissions') }}', type: 'error' } }));
})
.finally(() => {
this.isPermissionsLoading = false;
});
},
togglePermission(machineId) {
this.permissions = { ...this.permissions, [machineId]: !this.permissions[machineId] };
},
savePermissions() {
const machineIds = Object.keys(this.permissions).filter(id => this.permissions[id]);
fetch(`/admin/basic-settings/machines/permissions/accounts/${this.targetUserId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content,
'Accept': 'application/json'
},
body: JSON.stringify({ machine_ids: machineIds })
})
.then(res => res.json())
.then(data => {
if (data.success) {
window.dispatchEvent(new CustomEvent('toast', { detail: { message: data.message, type: 'success' } }));
setTimeout(() => window.location.reload(), 500);
} else {
throw new Error(data.error || 'Update failed');
}
})
.catch(e => {
window.dispatchEvent(new CustomEvent('toast', { detail: { message: e.message, type: 'error' } }));
});
}
}" @execute-regenerate.window="executeRegeneration($event.detail)">
<!-- 1. Header Area -->
@@ -150,6 +216,10 @@
class="px-8 py-3 rounded-xl text-sm font-black uppercase tracking-widest transition-all {{ $tab === 'models' ? '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' }}">
{{ __('Models') }}
</a>
<a href="{{ route('admin.basic-settings.machines.index', ['tab' => 'accounts']) }}"
class="px-8 py-3 rounded-xl text-sm font-black uppercase tracking-widest transition-all {{ $tab === 'accounts' ? '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' }}">
{{ __('Authorized Accounts Tab') }}
</a>
</div>
<!-- 2. Main Content Card -->
@@ -167,7 +237,7 @@
</svg>
</span>
<input type="text" name="search" value="{{ request('search') }}"
placeholder="{{ $tab === 'machines' ? __('Search machines...') : __('Search models...') }}"
placeholder="{{ $tab === 'machines' ? __('Search machines...') : ($tab === 'models' ? __('Search models...') : __('Search accounts...')) }}"
class="luxury-input py-2.5 pl-12 pr-6 block w-64">
</form>
</div>
@@ -318,6 +388,82 @@
{{ $machines->appends(['tab' => 'machines'])->links('vendor.pagination.luxury') }}
</div>
@elseif($tab === 'accounts')
<!-- Accounts Table (Machine Selection Interface) -->
<div class="overflow-x-auto">
<table class="w-full text-left border-separate border-spacing-y-0">
<thead>
<tr class="bg-slate-50/50 dark:bg-slate-900/10">
<th class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">
{{ __('Account Info') }}</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">
{{ __('Affiliation') }}</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">
{{ __('Authorized Machines') }}</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">
{{ __('Action') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50 dark:divide-slate-800/80">
@forelse($users_list as $user)
<tr class="group hover:bg-slate-50/80 dark:hover:bg-slate-800/40 transition-all duration-300">
<td class="px-6 py-6 font-display">
<div class="flex items-center gap-4">
<div class="w-10 h-10 rounded-xl bg-slate-100 dark:bg-slate-800 flex items-center justify-center text-slate-400 border border-slate-200 dark:border-slate-700">
<svg class="size-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
</svg>
</div>
<div class="flex flex-col">
<span class="text-base font-extrabold text-slate-800 dark:text-slate-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors">{{ $user->name }}</span>
<span class="text-xs font-mono font-bold text-slate-500 tracking-widest uppercase">{{ $user->username }}</span>
</div>
</div>
</td>
<td class="px-6 py-6">
<span class="px-2.5 py-1 rounded-lg text-xs font-bold border border-sky-100 dark:border-sky-900/30 bg-sky-50 dark:bg-sky-900/20 text-sky-600 dark:text-sky-400 tracking-widest uppercase">
{{ $user->company->name ?? __('System') }}
</span>
</td>
<td class="px-6 py-6">
<div class="flex flex-wrap gap-2 justify-center lg:justify-start max-w-[400px] mx-auto lg:mx-0">
@forelse($user->machines as $m)
<div class="flex flex-col px-4 py-2.5 rounded-xl bg-slate-50 dark:bg-slate-800/40 border border-slate-100 dark:border-white/5 hover:border-cyan-500/30 transition-all duration-300 shadow-sm">
<span class="text-xs font-black text-slate-700 dark:text-slate-200 leading-tight">{{ $m->name }}</span>
<span class="text-[10px] font-mono font-bold text-cyan-500 tracking-tighter mt-1">{{ $m->serial_no }}</span>
</div>
@empty
<div class="w-full text-center lg:text-left">
<span class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest opacity-40 italic">-- {{ __('None') }} --</span>
</div>
@endforelse
</div>
</td>
<td class="px-6 py-6 text-right">
<button @click="openPermissionModal({{ json_encode(['id' => $user->id, 'name' => $user->name]) }})"
class="inline-flex items-center gap-2 px-4 py-2 rounded-xl bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 hover:bg-cyan-500 hover:text-white transition-all duration-300 text-xs font-black uppercase tracking-widest shadow-sm shadow-cyan-500/5 group/auth">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 00-2 2zm10-10V7a4 4 0 00-8 0v4h8z" /></svg>
<span>{{ __('Authorize Btn') }}</span>
</button>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-6 py-24 text-center">
<div class="flex flex-col items-center gap-3 opacity-20">
<svg class="size-16" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2m16-10a4 4 0 11-8 0 4 4 0 018 0zM23 21v-2a4 4 0 00-3-3.87m-4-12a4 4 0 010 7.75" /></svg>
<p class="text-slate-400 font-extrabold tracking-widest uppercase text-xs">{{ __('No accounts found') }}</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="mt-8 border-t border-slate-100/50 dark:border-slate-800/50 pt-6">
{{ $users_list->appends(['tab' => 'accounts'])->links('vendor.pagination.luxury') }}
</div>
@else
<!-- Model Table -->
<div class="overflow-x-auto">
@@ -797,7 +943,6 @@
</div>
<!-- Helper text -->
<div class="absolute bottom-8 left-1/2 -translate-x-1/2 text-white/40 text-[10px] font-bold uppercase tracking-[0.3em] pointer-events-none">
{{ __('Click anywhere to close') }}
</div>
</div>
@@ -1021,6 +1166,117 @@
:confirm-text="__('Yes, regenerate')"
/>
<!-- 5. Machine Permissions Modal (Migrated) -->
<template x-teleport='body'>
<div x-show='showPermissionModal' class='fixed inset-0 z-[160] overflow-y-auto' x-cloak>
<div class='flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0'>
<div x-show='showPermissionModal' @click='showPermissionModal = false'
x-transition:enter='ease-out duration-300' x-transition:enter-start='opacity-0'
x-transition:enter-end='opacity-100' x-transition:leave='ease-in duration-200'
x-transition:leave-start='opacity-100' x-transition:leave-end='opacity-0'
class='fixed inset-0 bg-slate-900/60 backdrop-blur-sm transition-opacity'></div>
<span class='hidden sm:inline-block sm:align-middle sm:h-screen'>&#8203;</span>
<div x-show='showPermissionModal'
x-transition:enter='ease-out duration-300'
x-transition:enter-start='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
x-transition:enter-end='opacity-100 translate-y-0 sm:scale-100'
x-transition:leave='ease-in duration-200'
x-transition:leave-start='opacity-100 translate-y-0 sm:scale-100'
x-transition:leave-end='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
class='inline-block px-8 py-10 text-left align-bottom transition-all transform luxury-card rounded-3xl dark:bg-slate-900 border-slate-200/50 dark:border-slate-700/50 shadow-2xl sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full overflow-hidden animate-luxury-in'>
<div class='flex justify-between items-center mb-8'>
<div>
<h3 class='text-2xl font-black text-slate-800 dark:text-white font-display tracking-tight'>{{ __('Authorized Machines Management') }}</h3>
<div class='flex items-center gap-2 mt-1'>
<span class='text-[10px] font-black text-slate-400 uppercase tracking-[0.2em]'>{{ __('Account') }}:</span>
<span class='text-xs font-bold text-cyan-500 uppercase tracking-widest' x-text='targetUserName'></span>
</div>
</div>
<button @click='showPermissionModal = false' class='text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 transition-colors bg-slate-50 dark:bg-slate-800 p-2 rounded-xl'>
<svg class='size-6' fill='none' stroke='currentColor' viewBox='0 0 24 24'><path stroke-linecap='round' stroke-linejoin='round' stroke-width='2.5' d='M6 18L18 6M6 6l12 12' /></svg>
</button>
</div>
<div class='relative min-h-[400px]'>
<div class='mb-6'>
<div class='relative group'>
<span class='absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none z-10'>
<svg class='size-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' 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='permissionSearchQuery' placeholder='{{ __("Search machines...") }}'
class='luxury-input py-3 pl-12 pr-6 block w-full text-sm' @click.stop>
</div>
</div>
<template x-if='isPermissionsLoading'>
<div class='absolute inset-0 flex items-center justify-center bg-white/50 dark:bg-slate-900/50 backdrop-blur-sm z-10 rounded-2xl'>
<div class='flex flex-col items-center gap-3'>
<div class='w-10 h-10 border-4 border-cyan-500/20 border-t-cyan-500 rounded-full animate-spin'></div>
<span class='text-[10px] font-black text-cyan-600 dark:text-cyan-400 uppercase tracking-[0.2em] animate-pulse'>{{ __('Syncing Permissions...') }}</span>
</div>
</div>
</template>
<div class='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 max-h-[450px] overflow-y-auto pr-2 custom-scrollbar p-1'>
<template x-for='machine in allMachines.filter(m => !permissionSearchQuery || m.name.toLowerCase().includes(permissionSearchQuery.toLowerCase()) || m.serial_no.toLowerCase().includes(permissionSearchQuery.toLowerCase()))' :key='machine.id'>
<div @click='togglePermission(machine.id)'
:class='permissions[machine.id] ? "border-cyan-500 bg-cyan-500/5 dark:bg-cyan-500/10 ring-1 ring-cyan-500/20" : "border-slate-100 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-600"'
class='p-4 rounded-2xl border-2 cursor-pointer transition-all duration-300 group relative overflow-hidden shadow-sm hover:shadow-md'>
<div class='flex flex-col relative z-10'>
<div class='flex items-center gap-2'>
<div class='size-2 rounded-full' :class='permissions[machine.id] ? "bg-cyan-500" : "bg-slate-300 dark:bg-slate-700"'></div>
<span class='text-sm font-extrabold truncate' :class='permissions[machine.id] ? "text-cyan-600 dark:text-cyan-400" : "text-slate-700 dark:text-slate-300"'
x-text='machine.name'></span>
</div>
<span class='text-[10px] font-mono font-bold text-slate-400 mt-2 tracking-widest uppercase'
x-text='machine.serial_no'></span>
</div>
<div class='absolute -right-2 -bottom-2 opacity-[0.03] text-slate-900 dark:text-white pointer-events-none group-hover:scale-110 transition-transform duration-700'>
<svg class='size-20' fill='currentColor' viewBox='0 0 24 24'>
<path d='M5 2h14c1.1 0 2 .9 2 2v16c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2zm0 2v16h14V4H5zm3 3h8v6H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2z'/>
</svg>
</div>
<div class='absolute top-4 right-4 animate-luxury-in' x-show='permissions[machine.id]'>
<div class='size-5 rounded-full bg-cyan-500 flex items-center justify-center shadow-lg shadow-cyan-500/30'>
<svg class='size-3 text-white' fill='none' stroke='currentColor' viewBox='0 0 24 24'><path stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M5 13l4 4L19 7' /></svg>
</div>
</div>
</div>
</template>
</div>
</div>
<div class='flex flex-col sm:flex-row justify-between items-center mt-10 pt-8 border-t border-slate-100 dark:border-slate-800 gap-6'>
<div class='flex items-center gap-3'>
<div class='flex -space-x-2'>
<template x-for='i in Math.min(3, Object.values(permissions).filter(v => v).length)' :key='i'>
<div class='size-6 rounded-full border-2 border-white dark:border-slate-900 bg-cyan-500 flex items-center justify-center'>
<svg class='size-3 text-white' fill='currentColor' viewBox='0 0 24 24'><path d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14.5v-9l6 4.5-6 4.5z'/></svg>
</div>
</template>
</div>
<p class='text-[10px] font-black text-slate-400 uppercase tracking-[0.2em]'>
{{ __('Selection') }}: <span class='text-cyan-500 text-xs' x-text='Object.values(permissions).filter(v => v).length'></span> / <span x-text='allMachines?.length || 0'></span> {{ __('Devices') }}
</p>
</div>
<div class='flex gap-4 w-full sm:w-auto'>
<button @click='showPermissionModal = false' class='flex-1 sm:flex-none btn-luxury-ghost px-8'>{{ __('Cancel') }}</button>
<button @click='savePermissions()' class='flex-1 sm:flex-none btn-luxury-primary px-12' :disabled='isPermissionsLoading'>
<span>{{ __('Update Authorization') }}</span>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
</div>
@endsection

View File

@@ -8,11 +8,14 @@
id: '',
name: '',
code: '',
original_type: 'lease',
current_type: 'lease',
tax_id: '',
contact_name: '',
contact_phone: '',
contact_email: '',
valid_until: '',
start_date: '',
end_date: '',
status: 1,
note: '',
settings: {
@@ -23,8 +26,9 @@
openCreateModal() {
this.editing = false;
this.currentCompany = {
id: '', name: '', code: '', tax_id: '', contact_name: '', contact_phone: '',
contact_email: '', valid_until: '', status: 1, note: '',
id: '', name: '', code: '', original_type: 'lease', current_type: 'lease',
tax_id: '', contact_name: '', contact_phone: '',
contact_email: '', start_date: '', end_date: '', status: 1, note: '',
settings: { enable_material_code: false, enable_points: false }
};
this.showModal = true;
@@ -33,6 +37,8 @@
this.editing = true;
this.currentCompany = {
...company,
start_date: company.start_date ? company.start_date.substring(0, 10) : '',
end_date: company.end_date ? company.end_date.substring(0, 10) : '',
settings: {
enable_material_code: company.settings?.enable_material_code || false,
enable_points: company.settings?.enable_points || false
@@ -49,8 +55,9 @@
statusToggleSource: 'edit',
showDetail: false,
detailCompany: {
id: '', name: '', code: '', tax_id: '', contact_name: '', contact_phone: '',
contact_email: '', valid_until: '', status: 1, note: '',
id: '', name: '', code: '', original_type: 'lease', current_type: 'lease',
tax_id: '', contact_name: '', contact_phone: '', contact_email: '',
start_date: '', end_date: '', status: 1, note: '',
settings: { enable_material_code: false, enable_points: false },
users_count: 0, machines_count: 0
},
@@ -146,6 +153,9 @@
<th
class="px-6 py-4 text-[12px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest border-b border-slate-100 dark:border-slate-800">
{{ __('Customer Info') }}</th>
<th
class="px-6 py-4 text-[12px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest border-b border-slate-100 dark:border-slate-800 text-center">
{{ __('Business Type') }}</th>
<th
class="px-6 py-4 text-[12px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest border-b border-slate-100 dark:border-slate-800 text-center">
{{ __('Status') }}</th>
@@ -154,7 +164,7 @@
{{ __('Accounts / Machines') }}</th>
<th
class="px-6 py-4 text-[12px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest border-b border-slate-100 dark:border-slate-800 text-center">
{{ __('Valid Until') }}</th>
{{ __('Contract Period') }}</th>
<th
class="px-6 py-4 text-[12px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest border-b border-slate-100 dark:border-slate-800 text-right">
{{ __('Actions') }}</th>
@@ -174,9 +184,11 @@
</svg>
</div>
<div class="flex flex-col">
<span
class="text-base font-extrabold text-slate-800 dark:text-slate-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors">{{
$company->name }}</span>
<div class="flex items-center gap-2">
<span
class="text-base font-extrabold text-slate-800 dark:text-slate-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors">{{
$company->name }}</span>
</div>
<span
class="text-xs font-mono font-bold text-slate-500 dark:text-slate-400 mt-0.5 tracking-widest uppercase">{{
$company->code }}</span>
@@ -184,14 +196,28 @@
</div>
</td>
<td class="px-6 py-6 text-center">
<div class="flex flex-col items-center gap-2">
<div class="flex items-center gap-2 min-w-[100px] justify-center">
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-widest w-8 text-right">{{ __('Original:') }}</span>
<span class="px-2 py-0.5 rounded text-xs font-bold {{ $company->original_type === 'buyout' ? 'bg-amber-500/10 text-amber-600' : 'bg-blue-500/10 text-blue-600' }} uppercase tracking-wider">
{{ __($company->original_type === 'buyout' ? 'Buyout' : 'Lease') }}
</span>
</div>
<div class="flex items-center gap-2 min-w-[100px] justify-center">
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-widest w-8 text-right">{{ __('Current:') }}</span>
<span class="px-2 py-0.5 rounded text-xs font-bold {{ $company->current_type === 'buyout' ? 'bg-amber-500/10 text-amber-600 border border-amber-500/20' : 'bg-blue-500/10 text-blue-600 border border-blue-500/20' }} uppercase tracking-wider">
{{ __($company->current_type === 'buyout' ? 'Buyout' : 'Lease') }}
</span>
</div>
</div>
</td>
<td class="px-6 py-6 text-center py-6">
@if($company->status)
<span
class="inline-flex items-center px-3 py-1 rounded-full text-[11px] font-black bg-emerald-500/10 text-emerald-500 border border-emerald-500/20 tracking-widest uppercase">
<span class="inline-flex items-center px-3 py-1 rounded-full text-[11px] font-black bg-emerald-500/10 text-emerald-500 border border-emerald-500/20 tracking-widest uppercase">
{{ __('Active') }}
</span>
@else
<span
class="inline-flex items-center px-3 py-1 rounded-full text-[11px] font-black bg-rose-500/10 text-rose-500 border border-rose-500/20 tracking-widest uppercase">
<span class="inline-flex items-center px-3 py-1 rounded-full text-[11px] font-black bg-rose-500/10 text-rose-500 border border-rose-500/20 tracking-widest uppercase">
{{ __('Disabled') }}
</span>
@endif
@@ -209,11 +235,21 @@
</div>
</div>
</td>
<td class="px-6 py-6 text-center">
<span
class="text-[13px] font-bold font-display tracking-widest {{ $company->valid_until && $company->valid_until->isPast() ? 'text-rose-500' : 'text-slate-600 dark:text-slate-300' }}">
{{ $company->valid_until ? $company->valid_until->format('Y/m/d') : __('Permanent') }}
</span>
<td class="px-6 py-6 text-center text-slate-500 dark:text-slate-400">
<div class="flex flex-col items-center gap-1.5 font-mono">
<div class="flex items-center gap-2 min-w-[130px] justify-center">
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-tighter">{{ __('From:') }}</span>
<span class="text-[13px] font-bold tracking-tighter text-slate-600 dark:text-slate-300">
{{ $company->start_date ? $company->start_date->format('Y-m-d') : '--' }}
</span>
</div>
<div class="flex items-center gap-2 min-w-[130px] justify-center">
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-tighter">{{ __('To:') }}</span>
<span class="text-[13px] font-bold tracking-tighter {{ $company->end_date && $company->end_date->isPast() ? 'text-rose-500' : 'text-slate-800 dark:text-slate-200' }}">
{{ $company->end_date ? $company->end_date->format('Y-m-d') : __('Permanent') }}
</span>
</div>
</div>
</td>
<td class="px-6 py-6 text-right">
<div class="flex items-center justify-end gap-x-2">
@@ -342,16 +378,63 @@
__('Company Information') }}</h4>
</div>
<!-- Business Type Selector -->
<div class="p-4 rounded-2xl bg-slate-50/50 dark:bg-slate-800/50 border border-slate-100 dark:border-slate-700/50 space-y-4">
<label class="text-[11px] font-black text-slate-500 uppercase tracking-widest pl-1">
{{ __('Business Type') }}
<span class="text-rose-500 ml-0.5">*</span>
</label>
<!-- 新增模式:顯示切換按鈕 -->
<div x-show="!editing" class="flex p-1.5 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-700 w-fit">
<button type="button" @click="currentCompany.original_type = 'lease'"
:class="currentCompany.original_type === 'lease' ? 'bg-blue-500 text-white shadow-lg shadow-blue-500/20' : 'text-slate-400 hover:text-slate-600'"
class="px-4 py-1.5 rounded-lg text-xs font-bold uppercase tracking-widest transition-all">
{{ __('Lease') }}
</button>
<button type="button" @click="currentCompany.original_type = 'buyout'"
:class="currentCompany.original_type === 'buyout' ? 'bg-amber-500 text-white shadow-lg shadow-amber-500/20' : 'text-slate-400 hover:text-slate-600'"
class="px-4 py-1.5 rounded-lg text-xs font-bold uppercase tracking-widest transition-all">
{{ __('Buyout') }}
</button>
<input type="hidden" name="original_type" :value="currentCompany.original_type">
</div>
<!-- 編輯模式:顯示原始類型固定值 + 當前類型切換 -->
<div x-show="editing" class="flex flex-col gap-5">
<div class="flex items-center gap-4">
<span class="text-[11px] font-black text-slate-400 uppercase tracking-widest">{{ __('Original Type') }}: <span class="text-rose-500 ml-0.5">*</span></span>
<span class="px-4 py-1.5 bg-slate-100 dark:bg-slate-800 rounded-lg text-xs font-bold uppercase tracking-widest text-slate-600 dark:text-slate-400" x-text="currentCompany.original_type === 'buyout' ? '{{ __('Buyout') }}' : '{{ __('Lease') }}'"></span>
</div>
<div class="flex items-center gap-4">
<span class="text-[11px] font-black text-slate-400 uppercase tracking-widest">{{ __('Current Type') }}: <span class="text-rose-500 ml-0.5">*</span></span>
<div class="flex p-1.5 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-700 w-fit">
<button type="button" @click="currentCompany.current_type = 'lease'"
:class="currentCompany.current_type === 'lease' ? 'bg-blue-500 text-white shadow-lg shadow-blue-500/20' : 'text-slate-400 hover:text-slate-600'"
class="px-4 py-1.5 rounded-lg text-xs font-bold uppercase tracking-widest transition-all">
{{ __('Lease') }}
</button>
<button type="button" @click="currentCompany.current_type = 'buyout'"
:class="currentCompany.current_type === 'buyout' ? 'bg-amber-500 text-white shadow-lg shadow-amber-500/20' : 'text-slate-400 hover:text-slate-600'"
class="px-4 py-1.5 rounded-lg text-xs font-bold uppercase tracking-widest transition-all">
{{ __('Buyout') }}
</button>
<input type="hidden" name="current_type" :value="currentCompany.current_type">
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{
__('Company Name') }}</label>
__('Company Name') }} <span class="text-rose-500 ml-0.5">*</span></label>
<input type="text" name="name" x-model="currentCompany.name" required
class="luxury-input w-full" placeholder="{{ __('e.g. Taiwan Star') }}">
</div>
<div class="space-y-2">
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{
__('Company Code') }}</label>
__('Company Code') }} <span class="text-rose-500 ml-0.5">*</span></label>
<input type="text" name="code" x-model="currentCompany.code" required
class="luxury-input w-full" placeholder="{{ __('e.g. TWSTAR') }}">
</div>
@@ -364,11 +447,19 @@
<input type="text" name="tax_id" x-model="currentCompany.tax_id"
class="luxury-input w-full">
</div>
<div class="space-y-2">
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{
__('Contract Until (Optional)') }}</label>
<input type="date" name="valid_until" x-model="currentCompany.valid_until"
class="luxury-input w-full">
<div class="grid grid-cols-2 gap-3">
<div class="space-y-2">
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{
__('Start Date') }} <span class="text-rose-500 ml-0.5">*</span></label>
<input type="date" name="start_date" x-model="currentCompany.start_date" required
class="luxury-input w-full px-2">
</div>
<div class="space-y-2">
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{
__('End Date') }}</label>
<input type="date" name="end_date" x-model="currentCompany.end_date"
class="luxury-input w-full px-2">
</div>
</div>
</div>
</div>
@@ -419,7 +510,7 @@
</div>
<!-- Contact Section -->
<div class="space-y-6 pt-6 border-t border-slate-100 dark:border-slate-800 relative z-10">
<div class="space-y-6 pt-6 border-t border-slate-100 dark:border-slate-800 relative z-20">
<div class="flex items-center gap-3">
<div class="h-6 w-1 bg-amber-500 rounded-full"></div>
<h4 class="text-xs font-black text-slate-800 dark:text-white uppercase tracking-widest">{{
@@ -446,17 +537,10 @@
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{
__('Status') }}</label>
<x-searchable-select
id="company-status"
name="status"
x-model="currentCompany.status"
:hasSearch="false"
x-init="$watch('currentCompany.status', (value) => {
$nextTick(() => {
const inst = HSSelect.getInstance($el);
if (inst) {
inst.setValue(String(value));
}
});
})"
>
<option value="1">{{ __('Active') }}</option>
<option value="0">{{ __('Disabled') }}</option>
@@ -472,7 +556,7 @@
</div>
<!-- Feature Toggles Section -->
<div class="space-y-6 pt-6 border-t border-slate-100 dark:border-slate-800 relative z-10">
<div class="space-y-6 pt-6 border-t border-slate-100 dark:border-slate-800 relative z-[5]">
<div class="flex items-center gap-3">
<div class="h-6 w-1 bg-cyan-500 rounded-full"></div>
<h4 class="text-xs font-black text-slate-800 dark:text-white uppercase tracking-widest">{{ __('Feature Settings') }}</h4>
@@ -574,25 +658,57 @@
<!-- Validity & Status Section -->
<section class="space-y-4">
<h3 class="text-xs font-black text-emerald-500 uppercase tracking-[0.3em]">{{ __('Account Status') }}</h3>
<div class="grid grid-cols-2 gap-4">
<div class="bg-slate-50 dark:bg-slate-800/40 p-5 rounded-2xl border border-slate-100 dark:border-slate-800/80">
<span class="text-xs font-bold text-slate-400 uppercase tracking-widest block mb-1.5">{{ __('Current Status') }}</span>
<template x-if="detailCompany.status">
<div class="flex items-center gap-2">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.4)]"></span>
<span class="text-sm font-black text-emerald-500 uppercase tracking-widest">{{ __('Active') }}</span>
</div>
</template>
<template x-if="!detailCompany.status">
<div class="flex items-center gap-2">
<span class="w-1.5 h-1.5 rounded-full bg-rose-500 shadow-[0_0_8px_rgba(244,63,94,0.4)]"></span>
<span class="text-sm font-black text-rose-500 uppercase tracking-widest">{{ __('Disabled') }}</span>
</div>
</template>
<div class="grid grid-cols-1 gap-4">
<div class="bg-slate-50 dark:bg-slate-800/40 p-5 rounded-2xl border border-slate-100 dark:border-slate-800/80 flex items-center justify-between">
<div>
<span class="text-xs font-bold text-slate-400 uppercase tracking-widest block mb-1.5">{{ __('Current Status') }}</span>
<template x-if="detailCompany.status">
<div class="flex items-center gap-2">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.4)]"></span>
<span class="text-sm font-black text-emerald-500 uppercase tracking-widest">{{ __('Active') }}</span>
</div>
</template>
<template x-if="!detailCompany.status">
<div class="flex items-center gap-2">
<span class="w-1.5 h-1.5 rounded-full bg-rose-500 shadow-[0_0_8px_rgba(244,63,94,0.4)]"></span>
<span class="text-sm font-black text-rose-500 uppercase tracking-widest">{{ __('Disabled') }}</span>
</div>
</template>
</div>
</div>
<!-- Business Type -->
<div class="bg-slate-50 dark:bg-slate-800/40 p-5 rounded-2xl border border-slate-100 dark:border-slate-800/80">
<span class="text-xs font-bold text-slate-400 uppercase tracking-widest block mb-1.5">{{ __('Valid Until') }}</span>
<div class="text-sm font-black text-slate-700 dark:text-slate-300" x-text="detailCompany.valid_until ? detailCompany.valid_until : '{{ __('Permanent') }}'"></div>
<h4 class="text-[10px] font-black text-indigo-500 uppercase tracking-[0.2em] mb-4">{{ __('Business Type') }}</h4>
<div class="space-y-4">
<div class="flex items-center justify-between">
<span class="text-[11px] font-bold text-slate-400">{{ __('Original:') }}</span>
<span class="px-2.5 py-1 rounded-lg text-[11px] font-bold uppercase tracking-widest"
:class="detailCompany.original_type === 'buyout' ? 'bg-amber-500/10 text-amber-600' : 'bg-blue-500/10 text-blue-600'"
x-text="detailCompany.original_type === 'buyout' ? '{{ __('Buyout') }}' : '{{ __('Lease') }}'"></span>
</div>
<div class="flex items-center justify-between">
<span class="text-[11px] font-bold text-slate-400">{{ __('Current:') }}</span>
<span class="px-2.5 py-1 rounded-lg text-[11px] font-bold uppercase tracking-widest"
:class="detailCompany.current_type === 'buyout' ? 'bg-amber-500/10 text-amber-600 border border-amber-500/20' : 'bg-blue-500/10 text-blue-600 border border-blue-500/20'"
x-text="detailCompany.current_type === 'buyout' ? '{{ __('Buyout') }}' : '{{ __('Lease') }}'"></span>
</div>
</div>
</div>
<!-- Contract Period -->
<div class="bg-slate-50 dark:bg-slate-800/40 p-5 rounded-2xl border border-slate-100 dark:border-slate-800/80">
<h4 class="text-[10px] font-black text-indigo-500 uppercase tracking-[0.2em] mb-4">{{ __('Contract Period') }}</h4>
<div class="space-y-3 font-mono">
<div class="flex items-center gap-3">
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-tighter min-w-[32px]">{{ __('From:') }}</span>
<div class="text-[13px] font-bold tracking-tighter text-slate-700 dark:text-slate-200" x-text="detailCompany.start_date || '--'"></div>
</div>
<div class="flex items-center gap-3">
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-tighter min-w-[32px]">{{ __('To:') }}</span>
<div class="text-[13px] font-bold tracking-tighter text-slate-800 dark:text-white" :class="detailCompany.end_date_expired ? 'text-rose-500' : ''" x-text="detailCompany.end_date || '{{ __('Permanent') }}'"></div>
</div>
</div>
</div>
</div>
</section>

View File

@@ -6,21 +6,21 @@ $baseRoute = str_contains($routeName, 'sub-accounts') ? 'admin.data-config.sub-a
$tab = request('tab', 'accounts');
$roleSelectConfig = [
"placeholder" => __('Select Role'),
"hasSearch" => true,
"searchPlaceholder" => __('Search Role...'),
"isHidePlaceholder" => false,
"searchClasses" => "block w-[calc(100%-16px)] mx-2 py-2 px-3 text-sm border-slate-200 dark:border-white/10 rounded-lg focus:border-cyan-500 focus:ring-cyan-500 bg-slate-50 dark:bg-slate-900/50 dark:text-slate-200 placeholder:text-slate-400 dark:placeholder:text-slate-500",
"searchWrapperClasses" => "sticky top-0 bg-white/95 dark:bg-slate-900/95 backdrop-blur-md p-2 z-10",
"toggleClasses" => "hs-select-toggle luxury-select-toggle",
"dropdownClasses" => "hs-select-menu w-full bg-white/95 dark:bg-slate-900/95 backdrop-blur-xl border border-slate-200 dark:border-white/10 rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[100] animate-luxury-in",
"optionClasses" => "hs-select-option py-2.5 px-3 mb-0.5 text-sm text-slate-800 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-cyan-500/10 dark:hover:text-cyan-400 rounded-lg flex items-center justify-between transition-all duration-300",
"optionTemplate" => '<div class="flex items-center justify-between w-full"><span data-title></span><span class="hs-select-active-indicator hidden text-cyan-500"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></span></div>'
"placeholder" => __('Select Role'),
"hasSearch" => true,
"searchPlaceholder" => __('Search Role...'),
"isHidePlaceholder" => false,
"searchClasses" => "block w-[calc(100%-16px)] mx-2 py-2 px-3 text-sm border-slate-200 dark:border-white/10 rounded-lg focus:border-cyan-500 focus:ring-cyan-500 bg-slate-50 dark:bg-slate-900/50 dark:text-slate-200 placeholder:text-slate-400 dark:placeholder:text-slate-500",
"searchWrapperClasses" => "sticky top-0 bg-white/95 dark:bg-slate-900/95 backdrop-blur-md p-2 z-10",
"toggleClasses" => "hs-select-toggle luxury-select-toggle",
"dropdownClasses" => "hs-select-menu w-full bg-white/95 dark:bg-slate-900/95 backdrop-blur-xl border border-slate-200 dark:border-white/10 rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[100] animate-luxury-in",
"optionClasses" => "hs-select-option py-2.5 px-3 mb-0.5 text-sm text-slate-800 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-cyan-500/10 dark:hover:text-cyan-400 rounded-lg flex items-center justify-between transition-all duration-300",
"optionTemplate" => '<div class="flex items-center justify-between w-full"><span data-title></span><span class="hs-select-active-indicator hidden text-cyan-500"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></span></div>'
];
@endphp
@section('content')
<div class="space-y-2 pb-20" x-data="accountManager({
<div class="space-y-6 pb-20" x-data="accountManager({
roles: @js($roles),
errors: @js($errors->any()),
oldValues: {
@@ -46,36 +46,20 @@ $roleSelectConfig = [
</p>
</div>
<div class="flex items-center gap-3">
@if($tab === 'accounts')
<button @click="openCreateModal()" class="btn-luxury-primary">
<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="M12 4.5v15m7.5-7.5h-15" />
</svg>
<span>{{ __('Add Account') }}</span>
</button>
@endif
</div>
</div>
<!-- Tabs Navigation -->
<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">
<a href="{{ route($baseRoute, ['tab' => 'accounts']) }}"
class="px-8 py-3 rounded-xl text-sm font-black uppercase tracking-widest transition-all {{ $tab === 'accounts' ? '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' }}">
{{ __('Account Management') }}
</a>
<a href="{{ route($baseRoute, ['tab' => 'permissions']) }}"
class="px-8 py-3 rounded-xl text-sm font-black uppercase tracking-widest transition-all {{ $tab === 'permissions' ? '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' }}">
{{ __('Authorized Machines') }}
</a>
</div>
<!-- Accounts Content (Integrated Card) -->
<div class="luxury-card rounded-3xl p-8 animate-luxury-in mt-6">
<div class="luxury-card rounded-3xl p-8 animate-luxury-in">
<!-- Filters & Search -->
<form action="{{ route($baseRoute) }}" method="GET"
class="flex flex-col md:flex-row md:items-center gap-4 mb-10">
<input type="hidden" name="tab" value="{{ $tab }}">
<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"
@@ -101,7 +85,6 @@ $roleSelectConfig = [
</form>
<div class="overflow-x-auto">
@if($tab === 'accounts')
<table class="w-full text-left border-separate border-spacing-y-0">
<thead>
<tr class="bg-slate-50/50 dark:bg-slate-900/10">
@@ -146,8 +129,7 @@ $roleSelectConfig = [
</div>
<div class="flex flex-col">
<span
class="text-base font-extrabold text-slate-800 dark:text-slate-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors">{{
$user->name }}</span>
class="text-base font-extrabold text-slate-800 dark:text-slate-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors">{{ $user->name }}</span>
<span
class="text-xs font-bold text-slate-500 dark:text-slate-400 mt-0.5 tracking-widest uppercase"><span
class="font-mono">{{ $user->username }}</span></span>
@@ -166,12 +148,10 @@ $roleSelectConfig = [
<td class="px-6 py-6">
@if($user->company)
<span
class="text-xs font-bold text-slate-500 dark:text-slate-400 tracking-widest uppercase">{{
$user->company->name }}</span>
class="text-xs font-bold text-slate-500 dark:text-slate-400 tracking-widest uppercase">{{ $user->company->name }}</span>
@else
<span
class="px-2.5 py-1 rounded-lg text-xs font-bold bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 uppercase tracking-widest">{{
__('SYSTEM') }}</span>
class="px-2.5 py-1 rounded-lg text-xs font-bold bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 uppercase tracking-widest">{{ __('SYSTEM') }}</span>
@endif
</td>
@endif
@@ -224,7 +204,7 @@ $roleSelectConfig = [
<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" />
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>
<form action="{{ route($baseRoute . '.destroy', $user->id) }}" method="POST"
@@ -243,8 +223,7 @@ $roleSelectConfig = [
</form>
@else
<span
class="text-[10px] font-black text-slate-300 dark:text-slate-600 uppercase tracking-[0.15em] px-2">{{
__('Protected') }}</span>
class="text-[10px] font-black text-slate-300 dark:text-slate-600 uppercase tracking-[0.15em] px-2">{{ __('Protected') }}</span>
@endif
</div>
</td>
@@ -257,133 +236,17 @@ $roleSelectConfig = [
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2m16-10a4 4 0 11-8 0 4 4 0 018 0zM23 21v-2a4 4 0 00-3-3.87m-4-12a4 4 0 010 7.75" />
</svg>
<p class="text-slate-400 font-extrabold tracking-widest uppercase text-xs">{{ __('No
accounts found') }}</p>
<p class="text-slate-400 font-extrabold tracking-widest uppercase text-xs">{{ __('No accounts found') }}</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
@else
<table class="w-full text-left border-separate border-spacing-y-0">
<thead>
<tr class="bg-slate-50/50 dark:bg-slate-900/10">
<th
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">
{{ __('User Info') }}</th>
@if(auth()->user()->isSystemAdmin())
<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">
{{ __('Affiliation') }}</th>
@endif
<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">
{{ __('Authorized Machines') }}</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">
{{ __('Action') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50 dark:divide-slate-800/80">
@foreach ($users as $account)
<tr class="group hover:bg-slate-50/80 dark:hover:bg-slate-800/40 transition-all duration-300">
<td class="px-6 py-6">
<div class="flex items-center gap-x-4">
<div
class="w-10 h-10 rounded-xl bg-slate-100 dark:bg-slate-800 flex items-center justify-center text-slate-400 group-hover:bg-cyan-500 group-hover:text-white transition-all overflow-hidden shadow-sm">
@if($account->avatar)
<img src="{{ Storage::url($account->avatar) }}" class="w-full h-full object-cover">
@else
<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="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
</svg>
@endif
</div>
<div class="flex flex-col">
<span
class="text-base font-extrabold text-slate-800 dark:text-slate-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors">{{
$account->name }}</span>
<span
class="text-xs font-bold text-slate-500 dark:text-slate-400 mt-0.5 tracking-widest uppercase"><span
class="font-mono">{{ $account->username }}</span></span>
</div>
</div>
</td>
@if(auth()->user()->isSystemAdmin())
<td class="px-6 py-6">
@if($account->company)
<span
class="text-xs font-bold text-slate-500 dark:text-slate-400 tracking-widest uppercase">{{
$account->company->name }}</span>
@else
<span
class="px-2.5 py-1 rounded-lg text-xs font-bold bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 uppercase tracking-widest">{{
__('SYSTEM') }}</span>
@endif
</td>
@endif
<td class="px-6 py-6 min-w-[240px]">
<div class="flex flex-wrap gap-1.5 overflow-hidden items-center"
id="machines-container-{{ $account->id }}">
@if(!$account->company_id)
<span
class="px-3 py-1 text-[10px] bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 rounded-lg border border-cyan-500/20 uppercase font-black tracking-[0.2em] shadow-sm">
{{ __('Full Access') }}
</span>
@else
@php $assigned = $account->machines; @endphp
@if($assigned->isNotEmpty())
@foreach($assigned->take(3) as $machine)
<span
class="px-2 py-0.5 text-xs bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 rounded border border-slate-200 dark:border-slate-700 uppercase font-bold tracking-widest shadow-sm">
{{ $machine->name }}
</span>
@endforeach
@if($assigned->count() > 3)
<span
class="px-2 py-0.5 text-xs bg-cyan-50 dark:bg-cyan-500/10 text-cyan-500 dark:text-cyan-400 rounded border border-cyan-100 dark:border-cyan-500/20 uppercase font-bold tracking-widest shadow-sm cursor-help transition-all hover:bg-cyan-100 dark:hover:bg-cyan-500/20"
title="{{ $assigned->pluck('name')->implode(', ') }}">
+{{ $assigned->count() - 3 }}
</span>
@endif
@else
<span class="text-[10px] font-bold text-slate-400 italic">{{ __('No machines assigned')
}}</span>
@endif
@endif
</div>
</td>
<td class="px-6 py-6 text-right">
@if(!$account->company_id)
<span
class="text-[10px] font-black text-slate-300 dark:text-slate-600 uppercase tracking-[0.15em] px-2">
{{ __('System Default') }}
</span>
@else
<button @click="openMachineModal({{ $account->id }}, '{{ $account->name }}')"
class="btn-luxury-primary !px-4 !py-2 !text-[11px] !shadow-sm uppercase tracking-widest">
<svg class="w-3.5 h-3.5 mr-1.5 inline-block" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5"
d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4">
</path>
</svg>
<span class="align-middle">{{ __('Assign') }}</span>
</button>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
@endif
</div>
<div class="mt-8 border-t border-slate-100/50 dark:border-slate-800/50 pt-6">
{{ $users->appends(['tab' => $tab])->links('vendor.pagination.luxury') }}
{{ $users->links('vendor.pagination.luxury') }}
</div>
</div>
@@ -456,8 +319,7 @@ $roleSelectConfig = [
placeholder="{{ __('john@example.com') }}">
</div>
<div class="space-y-2">
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{
__('Phone') }}</label>
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Phone') }}</label>
<input type="text" name="phone" x-model="currentUser.phone"
class="luxury-input @error('phone') border-rose-500 @enderror">
</div>
@@ -465,12 +327,10 @@ $roleSelectConfig = [
@if(auth()->user()->isSystemAdmin())
<div class="space-y-2 mb-6 relative z-30">
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{
__('Affiliation') }}</label>
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Affiliation') }}</label>
<x-searchable-select id="modal-account-company" name="company_id"
placeholder="{{ __('SYSTEM') }}" x-model="currentUser.company_id"
@change="currentUser.company_id = $event.target.value; updateRoleSelect()">
{{-- 選項由組件根據 placeholder 自動生成 value=' ' 的項目 --}}
@foreach($companies as $company)
<option value="{{ $company->id }}" data-title="{{ $company->name }}">
{{ $company->name }}
@@ -486,12 +346,11 @@ $roleSelectConfig = [
{{ __('Role') }} <span class="text-rose-500">*</span>
</label>
<div id="role-select-wrapper" class="relative">
<!-- updateRoleSelect() 動態渲染 -->
<!-- Dynamic -->
</div>
</div>
<div class="space-y-2">
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{
__('Status') }}</label>
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Status') }}</label>
<x-searchable-select id="modal-account-status" name="status"
x-model="currentUser.status" :hasSearch="false">
<option value="1">{{ __('Active') }}</option>
@@ -502,8 +361,7 @@ $roleSelectConfig = [
<div class="space-y-2">
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">
<span
x-text="editing ? '{{ __('New Password (leave blank to keep current)') }}' : '{{ __('Password') }}'"></span>
<span x-text="editing ? '{{ __('New Password (leave blank to keep current)') }}' : '{{ __('Password') }}'"></span>
<template x-if="!editing">
<span class="text-rose-500">*</span>
</template>
@@ -514,8 +372,7 @@ $roleSelectConfig = [
</div>
<div class="flex justify-end gap-x-4 pt-8">
<button type="button" @click="showModal = false" class="btn-luxury-ghost px-8">{{
__('Cancel') }}</button>
<button type="button" @click="showModal = false" class="btn-luxury-ghost px-8">{{ __('Cancel') }}</button>
<button type="submit" class="btn-luxury-primary px-12">
<span x-text="editing ? '{{ __('Update') }}' : '{{ __('Create') }}'"></span>
</button>
@@ -526,97 +383,8 @@ $roleSelectConfig = [
</div>
</div>
<!-- Machine Assignment Modal -->
<template x-teleport="body">
<div x-show="showMachineModal"
class="fixed inset-0 z-[100] flex items-center justify-center overflow-y-auto px-4 py-8" x-cloak>
<div class="fixed inset-0 bg-slate-900/60 backdrop-blur-sm transition-opacity" x-show="showMachineModal"
x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
@click="showMachineModal = false"></div>
<div class="luxury-card !bg-white dark:!bg-slate-900 w-full max-w-2xl mx-auto rounded-[2.5rem] shadow-2xl relative overflow-hidden animate-luxury-in border border-slate-100 dark:border-slate-800"
@click.stop x-show="showMachineModal" x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0 scale-95" x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95">
<div
class="px-10 py-8 border-b border-slate-50 dark:border-slate-800/50 flex justify-between items-center relative z-10">
<div>
<h3 class="text-2xl font-black text-slate-800 dark:text-white tracking-tight font-display">{{
__('Assign Machines') }}</h3>
<p class="text-[11px] font-black text-cyan-500 uppercase tracking-[0.2em] mt-1"
x-text="selectedUserName"></p>
</div>
</div>
<div class="p-8 max-h-[60vh] overflow-y-auto custom-scrollbar">
<template x-if="loading">
<div class="flex flex-col items-center justify-center py-20">
<div
class="w-12 h-12 border-4 border-cyan-500/20 border-t-cyan-500 rounded-full animate-spin">
</div>
<p class="mt-4 text-xs font-bold text-slate-400 uppercase tracking-widest">{{ __('Loading
machines...') }}</p>
</div>
</template>
<template x-if="!loading && machines.length === 0">
<div class="text-center py-20 text-slate-400 font-bold uppercase tracking-widest">{{ __('No
machines available') }}</div>
</template>
<template x-if="!loading && machines.length > 0">
<div class="space-y-6">
<div class="flex items-center justify-between px-2">
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" @change="toggleAll" :checked="isAllSelected"
class="size-5 rounded-lg border-2 border-slate-300 dark:border-white/20 text-cyan-600 focus:ring-cyan-500 bg-transparent">
<span
class="text-xs font-black text-slate-500 dark:text-slate-400 uppercase tracking-widest group-hover:text-cyan-500 transition-colors">{{
__('Select All') }}</span>
</label>
<span class="text-[10px] font-black text-slate-400 uppercase tracking-widest"
x-text="`${assignedIds.length} / ${machines.length} {{ __('Selected') }}`"></span>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<template x-for="machine in machines" :key="machine.id">
<label
class="group relative flex items-center p-4 rounded-2xl bg-slate-50 dark:bg-slate-800/50 border-2 border-transparent hover:border-cyan-500/30 transition-all cursor-pointer has-[:checked]:border-cyan-500 has-[:checked]:bg-cyan-500/[0.03]">
<input type="checkbox" :value="machine.id.toString()" x-model="assignedIds"
class="size-5 rounded-lg border-2 border-slate-300 dark:border-white/20 text-cyan-600 focus:ring-cyan-500 bg-transparent">
<div class="ml-4 flex-1">
<div class="text-sm font-black text-slate-700 dark:text-slate-200 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors"
x-text="machine.name"></div>
<div class="text-[10px] font-mono font-bold text-slate-400 uppercase tracking-widest mt-0.5"
x-text="machine.serial_no"></div>
</div>
</label>
</template>
</div>
</div>
</template>
</div>
<div
class="px-10 py-6 bg-slate-50 dark:bg-slate-900/50 flex justify-end gap-3 border-t border-slate-100 dark:border-slate-800">
<button @click="showMachineModal = false" class="btn-luxury-ghost">{{ __('Cancel') }}</button>
<button @click="savePermissions" class="btn-luxury-primary px-8" :disabled="saving"
:class="saving ? 'opacity-50 cursor-not-allowed' : ''">
<template x-if="!saving"><span>{{ __('Save Changes') }}</span></template>
<template x-if="saving">
<div class="flex items-center gap-2">
<div class="w-4 h-4 border-2 border-white/20 border-t-white rounded-full animate-spin">
</div><span>{{ __('Saving...') }}</span>
</div>
</template>
</button>
</div>
</div>
</div>
</template>
<!-- Global Delete Confirm Modal -->
<x-delete-confirm-modal
:message="__('Are you sure you want to delete this account? This action cannot be undone.')" />
<!-- Status Change Confirm Modal -->
<!-- Modals -->
<x-delete-confirm-modal :message="__('Are you sure you want to delete this account? This action cannot be undone.')" />
<x-status-confirm-modal :title="__('Confirm Account Deactivation')" :message="__('Are you sure you want to deactivate this account? After deactivating, this account will no longer be able to log in to the system.')" />
<form x-ref="statusToggleForm" :action="toggleFormAction" method="POST" class="hidden">
@@ -631,7 +399,7 @@ $roleSelectConfig = [
document.addEventListener('alpine:init', () => {
Alpine.data('accountManager', (initData) => ({
showModal: initData.errors,
editing: initData.oldValues.method === 'PUT' || (initData.oldValues.id && initData.errors), // Added logic for editing when errors exist on an existing user
editing: initData.oldValues.method === 'PUT' || (initData.oldValues.id && initData.errors),
allRoles: initData.roles,
currentUser: {
id: initData.oldValues.id || '',
@@ -649,90 +417,12 @@ $roleSelectConfig = [
isStatusConfirmOpen: false,
toggleFormAction: '',
statusToggleSource: 'list',
confirmDelete(action) {
this.deleteFormAction = action;
this.isDeleteConfirmOpen = true;
},
tab: initData.tab,
showMachineModal: false,
selectedUserId: '',
selectedUserName: '',
machines: [],
assignedIds: [],
loading: false,
saving: false,
get isAllSelected() {
return this.machines.length > 0 && this.assignedIds.length === this.machines.length;
},
async openMachineModal(userId, userName) {
this.selectedUserId = userId;
this.selectedUserName = userName;
this.showMachineModal = true;
this.loading = true;
this.assignedIds = [];
try {
const url = `{{ route('admin.machines.permissions.accounts.get', 'USER_ID') }}`.replace('USER_ID', userId);
const response = await fetch(url);
const data = await response.json();
this.machines = data.machines;
this.assignedIds = data.assigned_ids.map(id => id.toString());
} catch (error) {
console.error('Error fetching data:', error);
window.Alpine.store('toast').show('{{ __("Failed to fetch machine data.") }}', 'error');
} finally {
this.loading = false;
}
},
toggleAll() {
if (this.isAllSelected) {
this.assignedIds = [];
} else {
this.assignedIds = this.machines.map(m => m.id.toString());
}
},
async savePermissions() {
this.saving = true;
try {
const url = `{{ route('admin.machines.permissions.accounts.sync', 'USER_ID') }}`.replace('USER_ID', this.selectedUserId);
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({ machine_ids: this.assignedIds })
});
const data = await response.json();
if (data.success) {
this.showMachineModal = false;
window.Alpine.store('toast').show(data.message, 'success');
const container = document.getElementById(`machines-container-${this.selectedUserId}`);
if (container) {
const assigned = data.assigned_machines;
if (assigned.length > 0) {
const visible = assigned.slice(0, 3);
const extraCount = assigned.length - 3;
const allNames = assigned.map(m => m.name).join(', ');
let html = visible.map(m => `<span class="px-2 py-0.5 text-xs bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 rounded border border-slate-200 dark:border-slate-700 uppercase font-bold tracking-widest shadow-sm">${m.name}</span>`).join('');
if (extraCount > 0) {
html += `<span class="px-2 py-0.5 text-xs bg-cyan-50 dark:bg-cyan-500/10 text-cyan-500 dark:text-cyan-400 rounded border border-cyan-100 dark:border-cyan-500/20 uppercase font-bold tracking-widest shadow-sm cursor-help transition-all hover:bg-cyan-100 dark:hover:bg-cyan-500/20" title="${allNames}">+${extraCount}</span>`;
}
container.innerHTML = html;
} else {
container.innerHTML = `<span class="text-[10px] font-bold text-slate-400 italic">{{ __('No machines assigned') }}</span>`;
}
}
} else {
window.Alpine.store('toast').show(data.error || '{{ __("Failed to save permissions.") }}', 'error');
}
} catch (error) {
console.error('Error saving permissions:', error);
window.Alpine.store('toast').show('{{ __("An error occurred while saving.") }}', 'error');
} finally {
this.saving = false;
}
},
submitConfirmedForm() {
if (this.statusToggleSource === 'list') {
this.$refs.statusToggleForm.submit();
@@ -740,35 +430,27 @@ $roleSelectConfig = [
this.$refs.accountForm.submit();
}
},
get filteredRoles() {
const companyId = this.currentUser.company_id;
if (!companyId || companyId.toString().trim() === '') {
// 系統管理層級:僅顯示全域角色 (company_id 為空)
if (!companyId || companyId.toString().trim() === '' || companyId === ' ') {
return this.allRoles.filter(r => !r.company_id || r.company_id.toString().trim() === '');
} else {
let companyRoles = this.allRoles.filter(r => r.company_id == companyId);
if (companyRoles.length > 0) {
return companyRoles;
} else {
// 租戶層級 fallback顯示全域角色但明確排除 super-admin
return this.allRoles.filter(r => (!r.company_id || r.company_id.toString().trim() === '') && r.name !== 'super-admin');
}
}
},
openCreateModal() {
this.editing = false;
const initialCompanyId = initData.oldValues.company_id;
const initialCompanyId = initData.oldValues.company_id || '';
let initialRole = '';
let roles = [];
if (!initialCompanyId || initialCompanyId.toString().trim() === '') {
roles = this.allRoles.filter(r => !r.company_id || r.company_id.toString().trim() === '');
} else {
let companyRoles = this.allRoles.filter(r => r.company_id == initialCompanyId);
// 這裡也要同步排除 super-admin
roles = companyRoles.length > 0 ? companyRoles : this.allRoles.filter(r => (!r.company_id || r.company_id.toString().trim() === '') && r.name !== 'super-admin');
}
const roles = this.filteredRoles;
if (roles.length > 0) {
initialRole = roles[0].name;
}
@@ -788,6 +470,7 @@ $roleSelectConfig = [
this.updateRoleSelect();
});
},
openEditModal(user) {
this.editing = true;
this.currentUser = {
@@ -803,6 +486,7 @@ $roleSelectConfig = [
this.updateRoleSelect();
});
},
syncSelect(id, value) {
this.$nextTick(() => {
const el = document.getElementById(id);
@@ -817,12 +501,12 @@ $roleSelectConfig = [
}
});
},
updateRoleSelect() {
this.$nextTick(() => {
const wrapper = document.getElementById('role-select-wrapper');
if (!wrapper) return;
// 🛡️ 終極防護:自動過濾配置中的換行符號,防止自動排版工具折行導致 Preline 崩潰
const cleanConfig = JSON.parse(JSON.stringify(this.roleSelectConfig), (key, value) => {
return typeof value === 'string' ? value.replace(/\r?\n|\r/g, ' ').trim() : value;
});
@@ -869,7 +553,6 @@ $roleSelectConfig = [
}
wrapper.appendChild(selectEl);
selectEl.addEventListener('change', (e) => {
this.currentUser.role = e.target.value;
});
@@ -879,7 +562,6 @@ $roleSelectConfig = [
const waitForHSSelect = (attempts = 0) => {
if (currentGen !== this._roleGeneration) return;
const select = window.HSSelect ? window.HSSelect.getInstance(selectEl) : null;
if (select) {
select.setValue(this.currentUser.role || '');
@@ -890,7 +572,6 @@ $roleSelectConfig = [
const initPreline = (attempts = 0) => {
if (currentGen !== this._roleGeneration) return;
if (window.HSStaticMethods && window.HSStaticMethods.autoInit) {
try {
window.HSStaticMethods.autoInit(['select']);
@@ -902,10 +583,10 @@ $roleSelectConfig = [
setTimeout(() => initPreline(attempts + 1), 50);
}
};
initPreline();
});
},
init() {
this.$watch('currentUser.company_id', (value) => {
this.syncSelect('modal-account-company', value);

View File

@@ -21,7 +21,7 @@
"searchClasses" => "block w-[calc(100%-16px)] mx-2 py-2 px-3 text-sm border-slate-200 dark:border-white/10 rounded-lg focus:border-cyan-500 focus:ring-cyan-500 bg-slate-50 dark:bg-slate-900/50 dark:text-slate-200 placeholder:text-slate-400 dark:placeholder:text-slate-500",
"searchWrapperClasses" => "sticky top-0 bg-white/95 dark:bg-slate-900/95 backdrop-blur-md p-2 z-10",
"toggleClasses" => "hs-select-toggle luxury-select-toggle",
"dropdownClasses" => "hs-select-menu w-full bg-white/95 dark:bg-slate-900/95 backdrop-blur-xl border border-slate-200 dark:border-white/10 rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[100] animate-luxury-in",
"dropdownClasses" => "hs-select-menu w-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-white/10 rounded-xl shadow-[0_20px_50px_rgba(0,0,0,0.3)] mt-2 z-[100] animate-luxury-in",
"optionClasses" => "hs-select-option py-2.5 px-3 mb-0.5 text-sm text-slate-800 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-cyan-500/10 dark:hover:text-cyan-400 rounded-lg flex items-center justify-between transition-all duration-300",
"optionTemplate" => '<div class="flex items-center justify-between w-full"><span data-title></span><span class="hs-select-active-indicator hidden text-cyan-500"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></span></div>'
];

View File

@@ -38,8 +38,6 @@ Route::middleware(['auth', 'verified', 'tenant.access'])->prefix('admin')->name(
Route::prefix('machines')->name('machines.')->group(function () {
// Route::get('/permissions', [App\Http\Controllers\Admin\MachineController::class , 'permissions'])->name('permissions'); // Merged into Sub-account Management
Route::get('/permissions/accounts/{user}', [App\Http\Controllers\Admin\MachineController::class, 'getAccountMachines'])->name('permissions.accounts.get');
Route::post('/permissions/accounts/{user}', [App\Http\Controllers\Admin\MachineController::class, 'syncAccountMachines'])->name('permissions.accounts.sync');
Route::get('/utilization', [App\Http\Controllers\Admin\MachineController::class , 'utilization'])->name('utilization');
Route::get('/utilization-ajax/{id?}', [App\Http\Controllers\Admin\MachineController::class, 'utilizationData'])->name('utilization-ajax');
Route::get('/{machine}/slots-ajax', [App\Http\Controllers\Admin\MachineController::class, 'slotsAjax'])->name('slots-ajax');
@@ -187,6 +185,10 @@ Route::middleware(['auth', 'verified', 'tenant.access'])->prefix('admin')->name(
Route::put('/{machine}', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'update'])->name('update');
Route::post('/', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'store'])->name('store');
Route::post('/{machine}/regenerate-token', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'regenerateToken'])->name('regenerate-token');
// 權限管理 (從 MachineController 遷移)
Route::get('/permissions/accounts/{user}', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'getAccountMachines'])->name('permissions.accounts.get');
Route::post('/permissions/accounts/{user}', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'syncAccountMachines'])->name('permissions.accounts.sync');
});
// 客戶金流設定