From 740eaa30b70d14d0198b6d3f9e90e3a0536efb57 Mon Sep 17 00:00:00 2001 From: sky121113 Date: Fri, 27 Mar 2026 13:43:08 +0800 Subject: [PATCH] =?UTF-8?q?[FEAT]=20=E5=84=AA=E5=8C=96=E5=BE=8C=E7=AB=AF?= =?UTF-8?q?=E5=B8=B3=E8=99=9F=E6=AC=8A=E9=99=90=E9=82=8F=E8=BC=AF=E3=80=81?= =?UTF-8?q?=E9=96=8B=E7=99=BC=E5=95=86=E5=93=81=E7=AE=A1=E7=90=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=8F=8A=E8=81=AF=E7=B5=A1=E8=B3=87=E8=A8=8A=20UI=20?= =?UTF-8?q?=E6=94=B9=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Admin/CompanyController.php | 20 + .../Admin/PermissionController.php | 14 +- .../Controllers/Admin/ProductController.php | 127 +++- app/Models/System/Translation.php | 3 +- database/factories/System/CompanyFactory.php | 24 + ...27_090740_increase_company_code_length.php | 28 + ...0_add_company_id_to_translations_table.php | 28 + ...e_image_to_image_url_in_products_table.php | 28 + lang/en.json | 21 +- lang/ja.json | 27 +- lang/zh_TW.json | 28 +- .../basic-settings/machines/index.blade.php | 46 +- .../views/admin/companies/index.blade.php | 262 ++++++++- .../admin/data-config/accounts.blade.php | 14 +- .../admin/permission/roles-edit.blade.php | 2 - .../views/admin/permission/roles.blade.php | 2 +- .../admin/products/create-modal.blade.php | 201 ------- .../views/admin/products/create.blade.php | 437 ++++++++++++++ resources/views/admin/products/edit.blade.php | 432 ++++++++++++++ .../views/admin/products/index.blade.php | 541 +++++++----------- resources/views/components/toast.blade.php | 4 +- tests/Feature/Admin/CompanySettingsTest.php | 109 ++++ 22 files changed, 1783 insertions(+), 615 deletions(-) create mode 100644 database/factories/System/CompanyFactory.php create mode 100644 database/migrations/2026_03_27_090740_increase_company_code_length.php create mode 100644 database/migrations/2026_03_27_111530_add_company_id_to_translations_table.php create mode 100644 database/migrations/2026_03_27_114500_rename_image_to_image_url_in_products_table.php delete mode 100644 resources/views/admin/products/create-modal.blade.php create mode 100644 resources/views/admin/products/create.blade.php create mode 100644 resources/views/admin/products/edit.blade.php create mode 100644 tests/Feature/Admin/CompanySettingsTest.php diff --git a/app/Http/Controllers/Admin/CompanyController.php b/app/Http/Controllers/Admin/CompanyController.php index 67f213e..080bbef 100644 --- a/app/Http/Controllers/Admin/CompanyController.php +++ b/app/Http/Controllers/Admin/CompanyController.php @@ -55,6 +55,7 @@ class CompanyController extends Controller 'valid_until' => 'nullable|date', 'status' => 'required|boolean', 'note' => 'nullable|string', + 'settings' => 'nullable|array', // 帳號相關欄位 (可選) 'admin_username' => 'nullable|string|max:255|unique:users,username', 'admin_password' => 'nullable|string|min:8', @@ -62,6 +63,12 @@ class CompanyController extends Controller 'admin_role' => 'nullable|string|exists:roles,name', ]); + // 確保 settings 中的值為布林值 + if (isset($validated['settings'])) { + $validated['settings']['enable_material_code'] = filter_var($validated['settings']['enable_material_code'] ?? false, FILTER_VALIDATE_BOOLEAN); + $validated['settings']['enable_points'] = filter_var($validated['settings']['enable_points'] ?? false, FILTER_VALIDATE_BOOLEAN); + } + DB::transaction(function () use ($validated) { $company = Company::create([ 'name' => $validated['name'], @@ -73,6 +80,7 @@ class CompanyController extends Controller 'valid_until' => $validated['valid_until'] ?? null, 'status' => $validated['status'], 'note' => $validated['note'] ?? null, + 'settings' => $validated['settings'] ?? [], ]); // 如果有填寫帳號資訊,則建立管理員帳號 @@ -130,8 +138,15 @@ class CompanyController extends Controller 'valid_until' => 'nullable|date', 'status' => 'required|boolean', 'note' => 'nullable|string', + 'settings' => 'nullable|array', ]); + // 確保 settings 中的值為布林值,避免 JSON 存儲為字串導致前端判斷錯誤 + if (isset($validated['settings'])) { + $validated['settings']['enable_material_code'] = filter_var($validated['settings']['enable_material_code'] ?? false, FILTER_VALIDATE_BOOLEAN); + $validated['settings']['enable_points'] = filter_var($validated['settings']['enable_points'] ?? false, FILTER_VALIDATE_BOOLEAN); + } + $company->update($validated); // 分支邏輯:若停用客戶,連帶停用其所有帳號 @@ -168,6 +183,11 @@ class CompanyController extends Controller return redirect()->back()->with('error', __('Cannot delete company with active accounts.')); } + // 為了解決軟刪除導致的唯一索引佔用問題,刪除前先重新命名唯一欄位 + $timestamp = now()->getTimestamp(); + $company->code = $company->code . '.deleted.' . $timestamp; + $company->save(); + $company->delete(); return redirect()->back()->with('success', __('Customer deleted successfully.')); diff --git a/app/Http/Controllers/Admin/PermissionController.php b/app/Http/Controllers/Admin/PermissionController.php index 84c9c2f..8e88637 100644 --- a/app/Http/Controllers/Admin/PermissionController.php +++ b/app/Http/Controllers/Admin/PermissionController.php @@ -377,8 +377,8 @@ class PermissionController extends Controller { $user = \App\Models\System\User::findOrFail($id); - if ($user->hasRole('super-admin')) { - return redirect()->back()->with('error', __('System super admin accounts cannot be modified via this interface.')); + if ($user->hasRole('super-admin') && !auth()->user()->hasRole('super-admin')) { + return redirect()->back()->with('error', __('System super admin accounts can only be modified by other super admins.')); } $validated = $request->validate([ @@ -485,8 +485,8 @@ class PermissionController extends Controller { $user = \App\Models\System\User::findOrFail($id); - if ($user->hasRole('super-admin')) { - return redirect()->back()->with('error', __('System super admin accounts cannot be deleted.')); + if ($user->hasRole('super-admin') && !auth()->user()->hasRole('super-admin')) { + return redirect()->back()->with('error', __('System super admin accounts can only be deleted by other super admins.')); } if ($user->id === auth()->id()) { @@ -508,9 +508,9 @@ class PermissionController extends Controller { $user = \App\Models\System\User::findOrFail($id); - // 禁止切換 Super Admin 狀態 - if ($user->hasRole('super-admin')) { - return back()->with('error', __('Cannot change Super Admin status.')); + // 非超級管理員禁止切換 Super Admin 狀態 + if ($user->hasRole('super-admin') && !auth()->user()->hasRole('super-admin')) { + return back()->with('error', __('Only Super Admins can change other Super Admin status.')); } $user->status = $user->status ? 0 : 1; diff --git a/app/Http/Controllers/Admin/ProductController.php b/app/Http/Controllers/Admin/ProductController.php index a915a76..08a38d1 100644 --- a/app/Http/Controllers/Admin/ProductController.php +++ b/app/Http/Controllers/Admin/ProductController.php @@ -6,22 +6,21 @@ use App\Http\Controllers\Controller; use App\Models\Product\Product; use App\Models\Product\ProductCategory; use App\Models\System\Company; +use App\Models\System\Translation; +use App\Traits\ImageHandler; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; class ProductController extends Controller { + use \App\Traits\ImageHandler; public function index(Request $request) { $user = auth()->user(); $query = Product::with(['category', 'translations', 'company']); - // 租戶隔離由 Global Scope (TenantScoped) 處理,但系統管理員可額外篩選 - if ($user->isSystemAdmin() && $request->filled('company_id')) { - $query->where('company_id', $request->company_id); - } - // 搜尋 if ($request->filled('search')) { $search = $request->search; @@ -38,11 +37,23 @@ class ProductController extends Controller } $per_page = $request->input('per_page', 10); + + $companyId = $user->company_id; + if ($user->isSystemAdmin()) { + if ($request->filled('company_id')) { + $companyId = $request->company_id; + $query->where('company_id', $companyId); + } + } + $products = $query->latest()->paginate($per_page)->withQueryString(); $categories = ProductCategory::all(); $companies = $user->isSystemAdmin() ? Company::all() : collect(); - $companySettings = $user->company ? ($user->company->settings ?? []) : []; + // 系統管理員在過濾特定公司時,應顯示該公司的功能開關 (如物料代碼、點數規則) + $selectedCompany = $companyId ? Company::find($companyId) : $user->company; + $companySettings = $selectedCompany ? ($selectedCompany->settings ?? []) : []; + $routeName = 'admin.data-config.products.index'; return view('admin.products.index', [ @@ -54,6 +65,42 @@ class ProductController extends Controller ]); } + public function create(Request $request) + { + $user = auth()->user(); + $categories = ProductCategory::all(); + $companies = $user->isSystemAdmin() ? Company::all() : collect(); + + // If system admin, check if company_id is provided in URL to get settings + $companyId = $request->query('company_id') ?? $user->company_id; + $selectedCompany = $companyId ? Company::find($companyId) : $user->company; + $companySettings = $selectedCompany ? ($selectedCompany->settings ?? []) : []; + + return view('admin.products.create', [ + 'categories' => $categories, + 'companies' => $companies, + 'companySettings' => $companySettings, + ]); + } + + public function edit($id) + { + $user = auth()->user(); + $product = Product::with(['translations', 'company'])->findOrFail($id); + $categories = ProductCategory::all(); + $companies = $user->isSystemAdmin() ? Company::all() : collect(); + + // Use the product's company settings for editing + $companySettings = $product->company ? ($product->company->settings ?? []) : []; + + return view('admin.products.edit', [ + 'product' => $product, + 'categories' => $categories, + 'companies' => $companies, + 'companySettings' => $companySettings, + ]); + } + public function store(Request $request) { $validated = $request->validate([ @@ -71,13 +118,18 @@ class ProductController extends Controller 'member_price' => 'required|numeric|min:0', 'metadata' => 'nullable|array', 'is_active' => 'nullable|boolean', + 'company_id' => 'nullable|exists:companies,id', + 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', ]); try { DB::beginTransaction(); - $dictKey = (string) Str::uuid(); - $company_id = auth()->user()->company_id; + $dictKey = \Illuminate\Support\Str::uuid()->toString(); + // Determine company_id: prioritized from request (for sys admin) then from user + $company_id = (auth()->user()->isSystemAdmin() && $request->filled('company_id')) + ? $request->company_id + : auth()->user()->company_id; // Store translations foreach ($request->names as $locale => $name) { @@ -86,16 +138,25 @@ class ProductController extends Controller 'group' => 'product', 'key' => $dictKey, 'locale' => $locale, - 'text' => $name, + 'value' => $name, 'company_id' => $company_id, ]); } + + + $imageUrl = null; + if ($request->hasFile('image')) { + $path = $this->storeAsWebp($request->file('image'), 'products'); + $imageUrl = Storage::url($path); + } + $product = Product::create([ 'company_id' => $company_id, 'category_id' => $request->category_id, - 'name' => $request->names['zh_TW'], // Default name uses zh_TW + 'name' => $request->names['zh_TW'] ?? (collect($request->names)->first() ?? 'Untitled'), // Fallback if zh_TW is missing 'name_dictionary_key' => $dictKey, + 'image_url' => $imageUrl, 'barcode' => $request->barcode, 'spec' => $request->spec, 'manufacturer' => $request->manufacturer, @@ -118,14 +179,14 @@ class ProductController extends Controller ]); } - return redirect()->back()->with('success', __('Product created successfully')); + return redirect()->route('admin.data-config.products.index')->with('success', __('Product created successfully')); } catch (\Exception $e) { DB::rollBack(); if ($request->wantsJson()) { return response()->json(['success' => false, 'message' => $e->getMessage()], 500); } - return redirect()->back()->with('error', $e->getMessage()); + return redirect()->back()->with('error', $e->getMessage())->withInput(); } } @@ -148,13 +209,15 @@ class ProductController extends Controller 'member_price' => 'required|numeric|min:0', 'metadata' => 'nullable|array', 'is_active' => 'nullable|boolean', + 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', + 'remove_image' => 'nullable|boolean', ]); try { DB::beginTransaction(); - $dictKey = $product->name_dictionary_key; - $company_id = auth()->user()->company_id; + $dictKey = $product->name_dictionary_key ?: \Illuminate\Support\Str::uuid()->toString(); + $company_id = $product->company_id; // Update or Create translations foreach ($request->names as $locale => $name) { @@ -174,15 +237,15 @@ class ProductController extends Controller 'locale' => $locale, ], [ - 'text' => $name, + 'value' => $name, 'company_id' => $company_id, ] ); } - $product->update([ + $data = [ 'category_id' => $request->category_id, - 'name' => $request->names['zh_TW'], + 'name' => $request->names['zh_TW'] ?? ($product->name ?? 'Untitled'), 'barcode' => $request->barcode, 'spec' => $request->spec, 'manufacturer' => $request->manufacturer, @@ -193,7 +256,25 @@ class ProductController extends Controller 'member_price' => $request->member_price, 'metadata' => $request->metadata ?? [], 'is_active' => $request->boolean('is_active', true), - ]); + ]; + + if ($request->hasFile('image')) { + // Delete old image + if ($product->image_url) { + $oldPath = str_replace('/storage/', '', $product->image_url); + Storage::disk('public')->delete($oldPath); + } + $path = $this->storeAsWebp($request->file('image'), 'products'); + $data['image_url'] = Storage::url($path); + } elseif ($request->boolean('remove_image')) { + if ($product->image_url) { + $oldPath = str_replace('/storage/', '', $product->image_url); + Storage::disk('public')->delete($oldPath); + } + $data['image_url'] = null; + } + + $product->update($data); DB::commit(); @@ -205,14 +286,14 @@ class ProductController extends Controller ]); } - return redirect()->back()->with('success', __('Product updated successfully')); + return redirect()->route('admin.data-config.products.index')->with('success', __('Product updated successfully')); } catch (\Exception $e) { DB::rollBack(); if ($request->wantsJson()) { return response()->json(['success' => false, 'message' => $e->getMessage()], 500); } - return redirect()->back()->with('error', $e->getMessage()); + return redirect()->back()->with('error', $e->getMessage())->withInput(); } } @@ -226,6 +307,12 @@ class ProductController extends Controller Translation::where('key', $product->name_dictionary_key)->delete(); } + // Delete image + if ($product->image_url) { + $oldPath = str_replace('/storage/', '', $product->image_url); + Storage::disk('public')->delete($oldPath); + } + $product->delete(); return redirect()->back()->with('success', __('Product deleted successfully')); diff --git a/app/Models/System/Translation.php b/app/Models/System/Translation.php index 64bba4e..157a1c5 100644 --- a/app/Models/System/Translation.php +++ b/app/Models/System/Translation.php @@ -7,9 +7,10 @@ use Illuminate\Database\Eloquent\Model; class Translation extends Model { - use HasFactory; + use HasFactory, \App\Traits\TenantScoped; protected $fillable = [ + 'company_id', 'group', 'key', 'locale', diff --git a/database/factories/System/CompanyFactory.php b/database/factories/System/CompanyFactory.php new file mode 100644 index 0000000..95889a2 --- /dev/null +++ b/database/factories/System/CompanyFactory.php @@ -0,0 +1,24 @@ + $this->faker->company, + 'code' => $this->faker->unique()->bothify('COMP###'), + 'status' => 1, + 'settings' => [ + 'enable_material_code' => false, + 'enable_points' => false, + ], + ]; + } +} diff --git a/database/migrations/2026_03_27_090740_increase_company_code_length.php b/database/migrations/2026_03_27_090740_increase_company_code_length.php new file mode 100644 index 0000000..ada7491 --- /dev/null +++ b/database/migrations/2026_03_27_090740_increase_company_code_length.php @@ -0,0 +1,28 @@ +string('code', 100)->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('companies', function (Blueprint $table) { + $table->string('code', 20)->change(); + }); + } +}; diff --git a/database/migrations/2026_03_27_111530_add_company_id_to_translations_table.php b/database/migrations/2026_03_27_111530_add_company_id_to_translations_table.php new file mode 100644 index 0000000..46c2ff4 --- /dev/null +++ b/database/migrations/2026_03_27_111530_add_company_id_to_translations_table.php @@ -0,0 +1,28 @@ +unsignedBigInteger('company_id')->nullable()->after('id')->index()->comment('所屬公司 ID'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('translations', function (Blueprint $table) { + $table->dropColumn('company_id'); + }); + } +}; diff --git a/database/migrations/2026_03_27_114500_rename_image_to_image_url_in_products_table.php b/database/migrations/2026_03_27_114500_rename_image_to_image_url_in_products_table.php new file mode 100644 index 0000000..2a5a974 --- /dev/null +++ b/database/migrations/2026_03_27_114500_rename_image_to_image_url_in_products_table.php @@ -0,0 +1,28 @@ +renameColumn('image', 'image_url'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('products', function (Blueprint $table) { + $table->renameColumn('image_url', 'image'); + }); + } +}; diff --git a/lang/en.json b/lang/en.json index 24b6b35..7bbfb2f 100644 --- a/lang/en.json +++ b/lang/en.json @@ -72,6 +72,7 @@ "Badge Settings": "Badge Settings", "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", @@ -110,6 +111,7 @@ "Connecting...": "Connecting...", "Connectivity Status": "Connectivity Status", "Connectivity vs Sales Correlation": "連線狀態與銷售關聯分析", + "Contact Info": "Contact Info", "Contact & Details": "Contact & Details", "Contact Email": "Contact Email", "Contact Name": "Contact Name", @@ -140,6 +142,7 @@ "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", @@ -212,6 +215,7 @@ "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", @@ -414,6 +418,7 @@ "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", @@ -596,6 +601,7 @@ "Update Password": "Update Password", "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", @@ -719,7 +725,7 @@ "Name in Japanese": "Name in Japanese", "Track Limit": "Track Limit", "Spring Limit": "Spring Limit", - "Inventory Limits Configuration": "Inventory Limits Configuration", + "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", @@ -738,5 +744,14 @@ "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?" -} \ No newline at end of file + "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" +} diff --git a/lang/ja.json b/lang/ja.json index f9fc476..b8a8ac1 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -72,6 +72,7 @@ "Badge Settings": "バッジ設定", "Basic Information": "基本情報", "Basic Settings": "基本設定", + "Basic Specifications": "基本仕様", "Batch No": "批號", "Belongs To": "所属", "Belongs To Company": "所属会社", @@ -110,6 +111,7 @@ "Connecting...": "接続中...", "Connectivity Status": "接続ステータス概況", "Connectivity vs Sales Correlation": "連線狀態與銷售關聯分析", + "Contact Info": "連絡先情報", "Contact & Details": "連絡先と詳細", "Contact Email": "連絡先メールアドレス", "Contact Name": "連絡担当者名", @@ -140,6 +142,7 @@ "Default Not Donate": "デフォルト寄付しない", "Define and manage security roles and permissions.": "システムのセキュリティロールと権限を定義および管理します。", "Define new third-party payment parameters": "新しいサードパーティ決済パラメータを定義", + "Feature Toggles": "機能トグル", "Delete": "削除", "Delete Account": "アカウントの削除", "Delete Permanently": "完全に削除", @@ -212,6 +215,7 @@ "Heating End Time": "加熱終了時間", "Heating Range": "加熱時間帯", "Heating Start Time": "加熱開始時間", + "Channel Limits": "スロット上限", "Helper": "ヘルパー", "Home Page": "主画面", "Machine Utilization": "機台稼働率", @@ -415,6 +419,7 @@ "Payment Configuration updated successfully.": "決済設定が正常に更新されました。", "Payment Selection": "決済選択", "Pending": "保留中", + "Pricing Information": "価格情報", "Performance": "效能 (Performance)", "Permanent": "永久認可", "Permanently Delete Account": "アカウントを永久に削除", @@ -483,7 +488,10 @@ "Role name already exists in this company.": "この会社には同じ名前のロールが既に存在します。", "Role not found.": "ロールが見つかりませんでした。", "Role updated successfully.": "ロールが正常に更新されました。", - "Roles": "ロール權限", + "Points Rule": "ポイントルール", + "Points Settings": "ポイント設定", + "Points toggle": "ポイント切り替え", + "Roles": "ロール権限", "Roles scoped to specific customer companies.": "適用於各個客戶單位的特定角色。", "Running Status": "稼働状況", "SYSTEM": "システムレベル", @@ -561,7 +569,8 @@ "Systems Initializing": "システム初期化中", "TapPay Integration": "TapPay 統合決済", "TapPay Integration Settings Description": "TapPay 決済連携設定", - "Target": "目標", + "Statistics": "統計データ", + "Target": "ターゲット", "Tax ID (Optional)": "納税者番号 (任意)", "Temperature": "温度", "TermID": "端末ID (TermID)", @@ -597,6 +606,7 @@ "Update Password": "パスワードの更新", "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": "一般ユーザー", @@ -720,7 +730,7 @@ "Name in Japanese": "日本語名", "Track Limit": "ベルトコンベア上限", "Spring Limit": "スプリング上限", - "Inventory Limits Configuration": "在庫上限設定", + "Channel Limits Configuration": "スロット上限設定", "Manage your catalog, prices, and multilingual details.": "カタログ、価格、多言語詳細を管理します。", "Product created successfully": "商品が正常に作成されました", "Product updated successfully": "商品が正常に更新されました", @@ -739,5 +749,12 @@ "Update Product": "商品を更新", "Edit Product": "商品を編集", "Create Product": "商品を作成", - "Are you sure you want to delete this product?": "この商品を削除してもよろしいですか?" -} \ No newline at end of file + "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)" +} diff --git a/lang/zh_TW.json b/lang/zh_TW.json index d8e75d4..5cb9118 100644 --- a/lang/zh_TW.json +++ b/lang/zh_TW.json @@ -72,6 +72,7 @@ "Badge Settings": "識別證", "Basic Information": "基本資訊", "Basic Settings": "基本設定", + "Basic Specifications": "基本規格", "Batch No": "批號", "Belongs To": "所屬單位", "Belongs To Company": "所屬單位", @@ -110,6 +111,7 @@ "Connecting...": "連線中", "Connectivity Status": "連線狀態概況", "Connectivity vs Sales Correlation": "連線狀態與銷售關聯分析", + "Contact Info": "聯絡資訊", "Contact & Details": "聯絡資訊與詳情", "Contact Email": "聯絡人信箱", "Contact Name": "聯絡人姓名", @@ -140,6 +142,7 @@ "Default Not Donate": "預設不捐贈", "Define and manage security roles and permissions.": "定義並管理系統安全角色與權限。", "Define new third-party payment parameters": "定義新的第三方支付參數", + "Feature Toggles": "功能開關", "Delete": "刪除", "Delete Account": "刪除帳號", "Delete Permanently": "確認永久刪除資料", @@ -215,6 +218,7 @@ "Heating End Time": "關閉-加熱時間", "Heating Range": "加熱時段", "Heating Start Time": "開啟-加熱時間", + "Channel Limits": "貨道上限", "Helper": "小幫手", "Home Page": "主頁面", "Info": "一般", @@ -405,6 +409,7 @@ "Payment Configuration updated successfully.": "金流設定已成功更新。", "Payment Selection": "付款選擇", "Pending": "待核效期", + "Pricing Information": "價格資訊", "Performance": "效能 (Performance)", "Permanent": "永久授權", "Permanently Delete Account": "永久刪除帳號", @@ -476,6 +481,9 @@ "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.": "適用於各個客戶單位的特定角色。", "Running Status": "運行狀態", @@ -557,7 +565,9 @@ "System": "系統", "TapPay Integration": "TapPay 支付串接", "TapPay Integration Settings Description": "喬睿科技支付串接設定", + "Statistics": "數據統計", "Target": "目標", + "Tax ID": "統一編號", "Tax ID (Optional)": "統一編號 (選填)", "Temperature": "溫度", "TermID": "終端代號 (TermID)", @@ -594,6 +604,7 @@ "Update Password": "更改密碼", "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": "一般用戶", @@ -719,7 +730,7 @@ "Name in Japanese": "日文名稱", "Track Limit": "履帶貨道上限", "Spring Limit": "彈簧貨道上限", - "Inventory Limits Configuration": "庫存上限配置", + "Channel Limits Configuration": "貨道上限配置", "Manage your catalog, prices, and multilingual details.": "管理您的商品型錄、價格及多語系詳情。", "Product created successfully": "商品已成功建立", "Product updated successfully": "商品已成功更新", @@ -740,7 +751,7 @@ "Edit Product": "編輯商品", "Create Product": "建立商品", "Are you sure you want to delete this product?": "您確定要刪除此商品嗎?", - "Limits (Track/Spring)": "庫存上限 (履帶/彈簧)", + "Channel Limits (Track/Spring)": "貨道上限 (履帶/彈簧)", "Member": "會員價", "Search products...": "搜尋商品...", "Product Info": "商品資訊", @@ -753,5 +764,14 @@ "Traditional Chinese": "繁體中文", "English": "英文", "Japanese": "日文", - "Select Category": "選擇類別" -} \ No newline at end of file + "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)" +} diff --git a/resources/views/admin/basic-settings/machines/index.blade.php b/resources/views/admin/basic-settings/machines/index.blade.php index b2db5a5..a9bf8ce 100644 --- a/resources/views/admin/basic-settings/machines/index.blade.php +++ b/resources/views/admin/basic-settings/machines/index.blade.php @@ -297,8 +297,8 @@ class="p-2 rounded-lg bg-slate-50 dark:bg-slate-800 text-slate-400 hover:text-cyan-500 hover:bg-cyan-500/5 dark:hover:bg-cyan-500/10 border border-transparent hover:border-cyan-500/20 transition-all inline-flex group/btn" title="{{ __('View Details') }}"> - + + @@ -870,10 +870,10 @@
+ class="px-6 py-3 border-b border-slate-100 dark:border-slate-800 flex items-center justify-between">

{{ __('Parameters') }}

-

-
+