diff --git a/.agents/rules/rbac-rules.md b/.agents/rules/rbac-rules.md index 229e5ed..c42fe21 100644 --- a/.agents/rules/rbac-rules.md +++ b/.agents/rules/rbac-rules.md @@ -72,7 +72,7 @@ trigger: always_on ### 5.1 初始角色建立 當系統管理員為新客戶(該租戶尚未有任何角色)建立第一個帳號時,應遵循以下邏輯: -1. **選取範本**:從系統預設的「全域角色範本」(`company_id = null` 且 `is_system = 0`)中選取一個作為基礎。 +1. **選取範本**:從系統預設的「全域角色範本」(`company_id = null` 且 `is_system = 1`)中選取一個作為基礎,但必須排除「超級管理員 (`super-admin`)」。 2. **自動克隆**:系統會將該範本的權限內容複製一份至該租戶下。 3. **統一命名**:克隆後的角色名稱在該租戶公司內應統一命名為**「管理員」**。 4. **帳號綁定**:該新客戶帳號將被指派至此新建立的「管理員」角色。 diff --git a/app/Http/Controllers/Admin/DataConfigController.php b/app/Http/Controllers/Admin/DataConfigController.php index cbe0c1c..bec29b9 100644 --- a/app/Http/Controllers/Admin/DataConfigController.php +++ b/app/Http/Controllers/Admin/DataConfigController.php @@ -25,15 +25,6 @@ class DataConfigController extends Controller ]); } - // 管理者可賣商品 - public function adminProducts() - { - return view('admin.placeholder', [ - 'title' => '商品狀態', - 'description' => '管理者商品銷售權限', - ]); - } - // 子帳號管理 public function subAccounts() diff --git a/app/Http/Controllers/Admin/PermissionController.php b/app/Http/Controllers/Admin/PermissionController.php index 8e88637..5b86526 100644 --- a/app/Http/Controllers/Admin/PermissionController.php +++ b/app/Http/Controllers/Admin/PermissionController.php @@ -315,8 +315,8 @@ class PermissionController extends Controller // 驗證角色與公司的匹配性 (RBAC Safeguard) if ($company_id !== null) { - // 如果是租戶帳號,不能選超級管理員角色 - if ($role->is_system && $role->name === 'super-admin') { + // 如果是租戶帳號,絕對不能指派超級管理員角色 (super-admin) + if ($role->name === 'super-admin') { return redirect()->back()->with('error', __('Super-admin role cannot be assigned to tenant accounts.')); } // 如果角色有特定的 company_id,必須匹配 @@ -324,7 +324,7 @@ class PermissionController extends Controller return redirect()->back()->with('error', __('This role belongs to another company and cannot be assigned.')); } } else { - // 如果是系統層級帳號,只能選系統角色 (is_system = 1) + // 如果是系統層級帳號,只能選全域系統角色 (is_system = 1) if (!$role->is_system) { return redirect()->back()->with('error', __('Only system roles can be assigned to platform administrative accounts.')); } @@ -408,7 +408,8 @@ class PermissionController extends Controller // 驗證角色與公司的匹配性 (RBAC Safeguard) if ($user->id !== auth()->id()) { // 排除編輯自己 (super-admin 有特殊邏輯) if ($target_company_id !== null) { - if ($roleObj->is_system && $roleObj->name === 'super-admin') { + // 租戶層級排除 super-admin + if ($roleObj->name === 'super-admin') { return redirect()->back()->with('error', __('Super-admin role cannot be assigned to tenant accounts.')); } if ($roleObj->company_id !== null && $roleObj->company_id != $target_company_id) { @@ -416,7 +417,7 @@ class PermissionController extends Controller } } else { if (!$roleObj->is_system) { - return redirect()->back()->with('error', __('Only system roles can be assigned to platform administrative accounts.')); + return redirect()->back()->with('error', __('Only global system roles can be assigned to platform administrative accounts.')); } } } diff --git a/app/Http/Controllers/Admin/ProductController.php b/app/Http/Controllers/Admin/ProductController.php index 08a38d1..d7fe895 100644 --- a/app/Http/Controllers/Admin/ProductController.php +++ b/app/Http/Controllers/Admin/ProductController.php @@ -297,6 +297,20 @@ class ProductController extends Controller } } + public function toggleStatus($id) + { + try { + $product = Product::findOrFail($id); + $product->is_active = !$product->is_active; + $product->save(); + + $status = $product->is_active ? __('Enabled') : __('Disabled'); + return redirect()->back()->with('success', __('Product status updated to :status', ['status' => $status])); + } catch (\Exception $e) { + return redirect()->back()->with('error', $e->getMessage()); + } + } + public function destroy($id) { try { diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php index 6d90a5c..89d7239 100644 --- a/database/seeders/RoleSeeder.php +++ b/database/seeders/RoleSeeder.php @@ -30,8 +30,12 @@ class RoleSeeder extends Seeder 'menu.analysis', 'menu.audit', 'menu.data-config', + 'menu.data-config.products', + 'menu.data-config.advertisements', 'menu.data-config.sub-accounts', 'menu.data-config.sub-account-roles', + 'menu.data-config.points', + 'menu.data-config.badges', 'menu.remote', 'menu.line', 'menu.reservation', @@ -72,8 +76,12 @@ class RoleSeeder extends Seeder 'menu.analysis', 'menu.audit', 'menu.data-config', + 'menu.data-config.products', + 'menu.data-config.advertisements', 'menu.data-config.sub-accounts', 'menu.data-config.sub-account-roles', + 'menu.data-config.points', + 'menu.data-config.badges', 'menu.remote', 'menu.line', 'menu.reservation', diff --git a/lang/en.json b/lang/en.json index 7bbfb2f..4223037 100644 --- a/lang/en.json +++ b/lang/en.json @@ -79,7 +79,6 @@ "Cancel": "Cancel", "Cancel Purchase": "Cancel Purchase", "Cannot Delete Role": "Cannot Delete Role", - "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", @@ -130,8 +129,7 @@ "Customer Info": "Customer Info", "Customer Management": "Customer Management", "Customer Payment Config": "Customer Payment Config", - "Customer created successfully.": "Customer created successfully.", - "Customer deleted successfully.": "Customer deleted successfully.", + "Customer created successfully.": "Customer created successfully", "Customer updated successfully.": "Customer updated successfully.", "Danger Zone: Delete Account": "Danger Zone: Delete Account", "Dashboard": "Dashboard", @@ -173,7 +171,6 @@ "Edit Role": "Edit Role", "Edit Role Permissions": "Edit Role Permissions", "Edit Settings": "Edit Settings", - "Edit Sub Account Role": "編輯子帳號角色", "Email": "Email", "Enabled/Disabled": "Enabled/Disabled", "Engineer": "Engineer", @@ -252,6 +249,7 @@ "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", @@ -272,6 +270,7 @@ "Line Permissions": "Line Permissions", "Line Products": "Line Products", "Loading machines...": "Loading machines...", + "Loyalty & Features": "Loyalty & Features", "Loading...": "Loading...", "Location": "Location", "Locked Page": "Locked Page", @@ -419,7 +418,7 @@ "Payment Selection": "Payment Selection", "Pending": "Pending", "Pricing Information": "Pricing Information", - "Performance": "效能 (Performance)", + "Performance": "Performance", "Permanent": "Permanent", "Permanently Delete Account": "Permanently Delete Account", "Permission Settings": "Permission Settings", @@ -702,6 +701,10 @@ "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.", @@ -753,5 +756,20 @@ "Customer Details": "Customer Details", "Current Status": "Current Status", "Product Image": "Product Image", - "PNG, JPG up to 2MB": "PNG, JPG up to 2MB" + "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." } diff --git a/lang/ja.json b/lang/ja.json index b8a8ac1..0321d9d 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -79,7 +79,6 @@ "Cancel": "キャンセル", "Cancel Purchase": "購入キャンセル", "Cannot Delete Role": "ロールを削除できません", - "Cannot delete company with active accounts.": "アクティブなアカウントを持つ会社は削除できません。", "Cannot delete model that is currently in use by machines.": "機台で使用中の型號は削除できません。", "Cannot delete role with active users.": "アクティブなユーザーがいるロールは削除できません。", "Card Reader": "カードリーダー", @@ -131,7 +130,6 @@ "Customer Management": "顧客管理", "Customer Payment Config": "決済設定管理", "Customer created successfully.": "顧客が正常に作成されました。", - "Customer deleted successfully.": "顧客が正常に削除されました。", "Customer updated successfully.": "顧客が正常に更新されました。", "Danger Zone: Delete Account": "危険区域:アカウントの削除", "Dashboard": "ダッシュボード", @@ -173,7 +171,6 @@ "Edit Role": "ロール編集", "Edit Role Permissions": "ロール権限の編集", "Edit Settings": "設定編集", - "Edit Sub Account Role": "編輯子帳號角色", "Email": "メールアドレス", "Enabled/Disabled": "有効/無効", "Engineer": "メンテナンス担当者", @@ -252,6 +249,7 @@ "Joined": "入会日", "Key": "キー (Key)", "Key No": "キー番号", + "Identity & Codes": "識別とコード", "LEVEL TYPE": "層級タイプ", "LINE Pay Direct": "LINE Pay 直結決済", "LINE Pay Direct Settings Description": "LINE Pay 公式直結設定", @@ -272,6 +270,7 @@ "Line Permissions": "Line管理權限", "Line Products": "Line商品", "Loading machines...": "正在載入機台...", + "Loyalty & Features": "ロイヤリティと機能", "Loading...": "読み込み中...", "Location": "場所", "Locked Page": "ロック画面", @@ -420,7 +419,8 @@ "Payment Selection": "決済選択", "Pending": "保留中", "Pricing Information": "価格情報", - "Performance": "效能 (Performance)", + "Channel Limits Configuration": "スロット上限設定", + "Performance": "パフォーマンス (Performance)", "Permanent": "永久認可", "Permanently Delete Account": "アカウントを永久に削除", "Permission Settings": "権限設定", @@ -707,6 +707,10 @@ "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.": "ステータスを変更してもよろしいですか?無効化後、このアカウントはシステムにログインできなくなります。", @@ -730,7 +734,6 @@ "Name in Japanese": "日本語名", "Track Limit": "ベルトコンベア上限", "Spring Limit": "スプリング上限", - "Channel Limits Configuration": "スロット上限設定", "Manage your catalog, prices, and multilingual details.": "カタログ、価格、多言語詳細を管理します。", "Product created successfully": "商品が正常に作成されました", "Product updated successfully": "商品が正常に更新されました", @@ -756,5 +759,20 @@ "Enable Points": "ポイントルールを有効化", "Show points rules in products": "商品情報にポイントルール相關フィールドを表示する", "Product Image": "商品画像", - "PNG, JPG up to 2MB": "PNG, JPG (最大 2MB)" + "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.": "顧客が正常に削除されました。" } diff --git a/lang/zh_TW.json b/lang/zh_TW.json index 5cb9118..f558a1e 100644 --- a/lang/zh_TW.json +++ b/lang/zh_TW.json @@ -79,7 +79,6 @@ "Cancel": "取消", "Cancel Purchase": "取消購買", "Cannot Delete Role": "無法刪除該角色", - "Cannot delete company with active accounts.": "無法刪除仍有帳號的客戶", "Cannot delete model that is currently in use by machines.": "無法刪除目前正在被機台使用的型號。", "Cannot delete role with active users.": "無法刪除已有綁定帳號的角色。", "Card Reader": "刷卡機", @@ -131,7 +130,6 @@ "Customer Management": "客戶管理", "Customer Payment Config": "客戶金流設定", "Customer created successfully.": "客戶新增成功", - "Customer deleted successfully.": "客戶刪除成功", "Customer updated successfully.": "客戶更新成功", "Danger Zone: Delete Account": "危險區域:刪除帳號", "Dashboard": "儀表板", @@ -173,7 +171,6 @@ "Edit Role": "編輯角色", "Edit Role Permissions": "編輯角色權限", "Edit Settings": "編輯設定", - "Edit Sub Account Role": "編輯子帳號角色", "Email": "電子郵件", "Enabled/Disabled": "啟用/停用", "Engineer": "維修人員", @@ -231,6 +228,7 @@ "Joined": "加入日期", "Key": "金鑰 (Key)", "Key No": "鑰匙編號", + "Identity & Codes": "識別與代碼", "LEVEL TYPE": "層級類型", "LINE Pay Direct": "LINE Pay 官方直連", "LINE Pay Direct Settings Description": "LINE Pay 官方直連設定", @@ -251,6 +249,7 @@ "Line Permissions": "Line 管理權限", "Line Products": "Line商品", "Loading machines...": "正在載入機台...", + "Loyalty & Features": "行銷與點數", "Loading...": "載入中...", "Location": "位置", "Locked Page": "鎖定頁", @@ -707,6 +706,10 @@ "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.": "您確定要變更狀態嗎?停用之後,該帳號將會立即被登出且無法再登入系統。", @@ -773,5 +776,20 @@ "Customer Details": "客戶詳情", "Current Status": "當前狀態", "Product Image": "商品圖片", - "PNG, JPG up to 2MB": "支援 PNG, JPG (最大 2MB)" + "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.": "客戶已成功刪除。" } diff --git a/resources/views/admin/companies/index.blade.php b/resources/views/admin/companies/index.blade.php index 5d1c93d..254a25a 100644 --- a/resources/views/admin/companies/index.blade.php +++ b/resources/views/admin/companies/index.blade.php @@ -232,7 +232,7 @@ class="p-2 rounded-lg bg-slate-50 dark:bg-slate-800 text-slate-400 hover:text-emerald-500 hover:bg-emerald-500/5 transition-all border border-transparent hover:border-emerald-500/20" title="{{ __('Enable') }}"> - + @endif diff --git a/resources/views/admin/data-config/accounts.blade.php b/resources/views/admin/data-config/accounts.blade.php index a85f079..016841b 100644 --- a/resources/views/admin/data-config/accounts.blade.php +++ b/resources/views/admin/data-config/accounts.blade.php @@ -743,13 +743,15 @@ $roleSelectConfig = [ get filteredRoles() { const companyId = this.currentUser.company_id; if (!companyId || companyId.toString().trim() === '') { + // 系統管理層級:僅顯示全域角色 (company_id 為空) 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 { - return this.allRoles.filter(r => !r.company_id || r.company_id.toString().trim() === ''); + // 租戶層級 fallback:顯示全域角色但明確排除 super-admin + return this.allRoles.filter(r => (!r.company_id || r.company_id.toString().trim() === '') && r.name !== 'super-admin'); } } }, @@ -763,7 +765,8 @@ $roleSelectConfig = [ roles = this.allRoles.filter(r => !r.company_id || r.company_id.toString().trim() === ''); } else { let companyRoles = this.allRoles.filter(r => r.company_id == initialCompanyId); - roles = companyRoles.length > 0 ? companyRoles : this.allRoles.filter(r => !r.company_id || r.company_id.toString().trim() === ''); + // 這裡也要同步排除 super-admin + roles = companyRoles.length > 0 ? companyRoles : this.allRoles.filter(r => (!r.company_id || r.company_id.toString().trim() === '') && r.name !== 'super-admin'); } if (roles.length > 0) { diff --git a/resources/views/admin/products/index.blade.php b/resources/views/admin/products/index.blade.php index c250f63..af2a419 100644 --- a/resources/views/admin/products/index.blade.php +++ b/resources/views/admin/products/index.blade.php @@ -122,8 +122,6 @@ $roleSelectConfig = [ @endif ${{ number_format($product->price, 0) }} - / - ${{ number_format($product->member_price, 0) }} {{ $product->track_limit }} @@ -132,19 +130,34 @@ $roleSelectConfig = [ @if($product->is_active) - {{ __('Active') }} + {{ __('Active') }} @else - {{ __('Disabled') }} + {{ __('Disabled') }} @endif
- + @if($product->is_active) + + @else + + @endif + @@ -169,7 +182,21 @@ $roleSelectConfig = [ - + + + + + +