diff --git a/app/Http/Controllers/Admin/PermissionController.php b/app/Http/Controllers/Admin/PermissionController.php index 3f1f538..383846a 100644 --- a/app/Http/Controllers/Admin/PermissionController.php +++ b/app/Http/Controllers/Admin/PermissionController.php @@ -42,20 +42,12 @@ class PermissionController extends Controller $permissionQuery->whereIn('name', $user->getAllPermissions()->pluck('name')); } + // 權限分組邏輯 $all_permissions = $permissionQuery->get() - ->filter(function($perm) { - // 排除子項項目,只顯示主要權限 - $excluded = [ - 'menu.basic.machines', - 'menu.basic.payment-configs', - 'menu.companies', - 'menu.accounts', - 'menu.roles', - ]; - return !in_array($perm->name, $excluded); - }) ->groupBy(function($perm) { if (str_starts_with($perm->name, 'menu.')) { + // 主選單權限:menu.xxx (兩段) + // 子選單權限:menu.xxx.yyy (三段) return 'menu'; } return 'other'; @@ -68,6 +60,60 @@ class PermissionController extends Controller return view('admin.permission.roles', compact('roles', 'all_permissions', 'title', 'currentUserRoleIds', 'companies')); } + /** + * Show the form for creating a new role. + */ + public function createRole() + { + $role = new \App\Models\System\Role(); + $user = auth()->user(); + + // 權限遞迴約束 + $permissionQuery = \Spatie\Permission\Models\Permission::query(); + if (!$user->isSystemAdmin()) { + $permissionQuery->whereIn('name', $user->getAllPermissions()->pluck('name')); + } + + $all_permissions = $permissionQuery->get()->groupBy(fn($p) => str_starts_with($p->name, 'menu.') ? 'menu' : 'other'); + + $title = request()->routeIs('*.sub-account-roles.create') ? __('Create Sub Account Role') : __('Create New Role'); + $back_url = request()->routeIs('*.sub-account-roles.create') ? route('admin.data-config.sub-account-roles') : route('admin.permission.roles'); + + return view('admin.permission.roles-edit', compact('role', 'all_permissions', 'title', 'back_url')); + } + + /** + * Show the form for editing the specified role. + */ + public function editRole($id) + { + $role = \App\Models\System\Role::findOrFail($id); + $user = auth()->user(); + + // 權限遞迴約束:租戶管理員只能看到並指派自己擁有的權限 + $permissionQuery = \Spatie\Permission\Models\Permission::query(); + if (!$user->isSystemAdmin()) { + $permissionQuery->whereIn('name', $user->getAllPermissions()->pluck('name')); + } + + // 權限分組邏輯 + $all_permissions = $permissionQuery->get() + ->groupBy(function($perm) { + if (str_starts_with($perm->name, 'menu.')) { + return 'menu'; + } + return 'other'; + }); + + // 根據路由決定標題 + $title = request()->routeIs('*.sub-account-roles.edit') ? __('Edit Sub Account Role') : __('Edit Role Permissions'); + + // 麵包屑/返回路徑 + $back_url = request()->routeIs('*.sub-account-roles.edit') ? route('admin.data-config.sub-account-roles') : route('admin.permission.roles'); + + return view('admin.permission.roles-edit', compact('role', 'all_permissions', 'title', 'back_url')); + } + /** * Store a newly created role in storage. */ @@ -97,7 +143,7 @@ class PermissionController extends Controller if (!empty($validated['permissions'])) { $perms = $validated['permissions']; - // 權限遞迴約束驗證:確保指派的權限是操作者權限的子集 + // 權限遞迴約束驗證 if (!auth()->user()->isSystemAdmin()) { $currentUserPerms = auth()->user()->getAllPermissions()->pluck('name'); if (collect($perms)->diff($currentUserPerms)->isNotEmpty()) { @@ -112,7 +158,8 @@ class PermissionController extends Controller $role->syncPermissions($perms); } - return redirect()->back()->with('success', __('Role created successfully.')); + $target_route = request()->routeIs('*.sub-account-roles.*') ? 'admin.data-config.sub-account-roles' : 'admin.permission.roles'; + return redirect()->route($target_route)->with('success', __('Role created successfully.')); } /** @@ -170,7 +217,8 @@ class PermissionController extends Controller } $role->syncPermissions($perms); - return redirect()->back()->with('success', __('Role updated successfully.')); + $target_route = request()->routeIs('*.sub-account-roles.*') ? 'admin.data-config.sub-account-roles' : 'admin.permission.roles'; + return redirect()->route($target_route)->with('success', __('Role updated successfully.')); } /** diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php index b4e4846..0d34b77 100644 --- a/database/seeders/RoleSeeder.php +++ b/database/seeders/RoleSeeder.php @@ -27,12 +27,19 @@ class RoleSeeder extends Seeder 'menu.analysis', 'menu.audit', 'menu.data-config', + 'menu.data-config.sub-accounts', + 'menu.data-config.sub-account-roles', 'menu.remote', 'menu.line', 'menu.reservation', 'menu.special-permission', 'menu.basic-settings', + 'menu.basic.machines', + 'menu.basic.payment-configs', 'menu.permissions', + 'menu.permissions.companies', + 'menu.permissions.accounts', + 'menu.permissions.roles', ]; foreach ($permissions as $permission) { @@ -59,6 +66,8 @@ class RoleSeeder extends Seeder 'menu.analysis', 'menu.audit', 'menu.data-config', + 'menu.data-config.sub-accounts', + 'menu.data-config.sub-account-roles', 'menu.remote', 'menu.line', 'menu.reservation', diff --git a/lang/en.json b/lang/en.json index a2cddaa..61f0164 100644 --- a/lang/en.json +++ b/lang/en.json @@ -479,5 +479,31 @@ "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.", "Your email address is unverified.": "Your email address is unverified.", - "Your recent account activity": "Your recent account activity" + "Your recent account activity": "Your recent account activity", + "menu.data-config": "Data Configuration", + "menu.data-config.sub-accounts": "Sub Account Management", + "menu.data-config.sub-account-roles": "Sub Account Roles", + "menu.basic-settings": "Basic Settings", + "menu.basic.machines": "Machine Settings", + "menu.basic.payment-configs": "Customer Payment Config", + "menu.permission": "Permission Settings", + "menu.permissions.companies": "Customer Management", + "menu.permissions.accounts": "Account Management", + "menu.permissions.roles": "Role Permissions", + "menu.members": "Member Management", + "menu.machines": "Machine Management", + "menu.app": "APP Management", + "menu.warehouses": "Warehouse Management", + "menu.sales": "Sales Management", + "menu.analysis": "Analysis Management", + "menu.audit": "Audit Management", + "menu.remote": "Remote Management", + "menu.line": "Line Management", + "menu.reservation": "Reservation System", + "menu.special-permission": "Special Permission", + "Edit Role Permissions": "Edit Role Permissions", + "Role Identification": "Role Identification", + "LEVEL TYPE": "LEVEL TYPE", + "Affiliated Unit": "Affiliated Unit", + "System Official": "System Official" } \ No newline at end of file diff --git a/lang/ja.json b/lang/ja.json index 6e95d62..11e9b7e 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -476,5 +476,31 @@ "You cannot assign permissions you do not possess.": "ご自身が所有していない権限を割り當てることはできません。", "You cannot delete your own account.": "ご自身のアカウントは削除できません。", "Your email address is unverified.": "メールアドレスが未確認です。", - "Your recent account activity": "最近のアカウントアクティビティ" + "Your recent account activity": "最近のアカウントアクティビティ", + "menu.data-config": "データ設定", + "menu.data-config.sub-accounts": "サブアカウント管理", + "menu.data-config.sub-account-roles": "サブアカウントロール", + "menu.basic-settings": "基本設定", + "menu.basic.machines": "機台設定", + "menu.basic.payment-configs": "決済設定管理", + "menu.permission": "權限設定", + "menu.permissions.companies": "顧客管理", + "menu.permissions.accounts": "アカウント管理", + "menu.permissions.roles": "ロール權限管理", + "menu.members": "会員管理", + "menu.machines": "機台管理", + "menu.app": "APP 運用", + "menu.warehouses": "倉庫管理", + "menu.sales": "売上レポート", + "menu.analysis": "データ分析", + "menu.audit": "監査管理", + "menu.remote": "リモート指令", + "menu.line": "LINE 設定", + "menu.reservation": "予約管理", + "menu.special-permission": "特殊権限", + "Edit Role Permissions": "ロール権限の編集", + "Role Identification": "ロール識別情報", + "LEVEL TYPE": "層級タイプ", + "Affiliated Unit": "所属ユニット", + "System Official": "公式" } \ No newline at end of file diff --git a/lang/zh_TW.json b/lang/zh_TW.json index 3c5951a..cada3a9 100644 --- a/lang/zh_TW.json +++ b/lang/zh_TW.json @@ -490,5 +490,39 @@ "You cannot assign permissions you do not possess.": "您無法指派您自身不具備的權限。", "You cannot delete your own account.": "您無法刪除自己的帳號。", "Your email address is unverified.": "您的電子郵件地址尚未驗證。", - "Your recent account activity": "最近的帳號活動" + "Your recent account activity": "最近的帳號活動", + "menu.members": "會員管理", + "menu.machines": "機台管理", + "menu.app": "APP 運維", + "menu.warehouses": "倉儲管理", + "menu.sales": "銷售報表", + "menu.analysis": "數據分析", + "menu.audit": "審核管理", + "menu.remote": "遠端指令", + "menu.line": "LINE 配置", + "menu.reservation": "預約管理", + "menu.special-permission": "特殊權限", + "menu.data-config": "資料設定", + "menu.data-config.sub-accounts": "子帳號管理", + "menu.data-config.sub-account-roles": "子帳號角色", + "menu.basic": "基本管理", + "menu.basic-settings": "基本設定", + "menu.basic.machines": "機台設定", + "menu.basic.payment-configs": "客戶金流設定", + "menu.permission": "權限設定", + "menu.permissions": "權限管理", + "menu.permissions.companies": "客戶管理", + "menu.permissions.accounts": "帳號管理", + "menu.permissions.roles": "角色權限管理", + "Select All": "全選", + "Deselect All": "取消全選", + "Sub-actions": "子項目", + "Quick Select": "快速選取", + "Total Selected": "已選擇總數", + "Scale level and access control": "層級與存取控制", + "Edit Role Permissions": "編輯角色權限", + "Role Identification": "角色識別資訊", + "LEVEL TYPE": "層級類型", + "Affiliated Unit": "所屬單位", + "System Official": "系統層" } \ No newline at end of file diff --git a/resources/views/admin/permission/roles-edit.blade.php b/resources/views/admin/permission/roles-edit.blade.php new file mode 100644 index 0000000..dd14bf8 --- /dev/null +++ b/resources/views/admin/permission/roles-edit.blade.php @@ -0,0 +1,308 @@ +@extends('layouts.admin') + +@section('content') +
+ +
+
+ + + +
+

+ {{ $title }} +

+
+ + {{ $role->name ?: __('New Role') }} + + @if($role->is_system) + + • {{ __('System Role') }} + + @endif +
+
+
+ +
+ +
+
@if(auth()->user()->roles->contains('id', $role->id)) +
+
+ +
+
+

{{ __('Modifying your own administrative permissions may result in losing access to certain system functions.') }}

+
+
+ @endif + + @php + $isSubAccountRole = request()->routeIs('*.sub-account-roles.*'); + if ($role->exists) { + $action = route($isSubAccountRole ? 'admin.data-config.sub-account-roles.update' : 'admin.permission.roles.update', $role->id); + $method = 'PUT'; + } else { + $action = route($isSubAccountRole ? 'admin.data-config.sub-account-roles.store' : 'admin.permission.roles.store'); + $method = 'POST'; + } + @endphp + +
+ @csrf + @if($method === 'PUT') + @method('PUT') + @endif + + + + + +
+ @foreach($all_permissions as $group => $permissions) + @php + $groupId = 'group-' . $group; + $groupPermissions = $permissions->pluck('name')->toArray(); + @endphp +
+
+
+ +

+ {{ __($group == 'menu' ? 'Menu Permissions' : 'Other Permissions') }} +

+
+ +
+ + @if($group === 'menu') +
+ @php + $parents = $permissions->filter(fn($p) => count(explode('.', $p->name)) === 2); + @endphp + + @foreach($parents as $parent) + @php + $children = $permissions->filter(function($p) use ($parent) { + if ($p->name === $parent->name) return false; + // 取得基礎前綴,例如 menu.basic-settings -> basic, menu.permission -> permission + $parentSlug = explode('.', $parent->name)[1] ?? ''; + $parentBase = rtrim(str_replace('-settings', '', $parentSlug), 's'); + + $childSlug = explode('.', $p->name)[1] ?? ''; + return str_starts_with($childSlug, $parentBase); + }); + @endphp + +
+
+
+ @if($children->count() > 0) + + @endif +
+

+ {{ __($parent->name) }} +

+ {{ $parent->name }} +
+
+ +
+ +
+
+ + @if($children->count() > 0) +
+
+ @foreach($children as $child) + + @endforeach +
+
+ @endif +
+ @endforeach +
+ @else +
+ @foreach($permissions as $perm) + + @endforeach +
+ @endif +
+ @endforeach + + +
+ {{ __('Cancel') }} + +
+
+
+
+@endsection + +@section('scripts') + +@endsection diff --git a/resources/views/admin/permission/roles.blade.php b/resources/views/admin/permission/roles.blade.php index 1832d9b..501d4ba 100644 --- a/resources/views/admin/permission/roles.blade.php +++ b/resources/views/admin/permission/roles.blade.php @@ -43,14 +43,12 @@

{{ $title }}

{{ __('Define and manage security roles and permissions.') }}

- + - -
@@ -119,7 +117,7 @@ @endif - +
@forelse($role->permissions->take(6) as $permission) {{ __(str_replace('menu.', '', $permission->name)) }} @@ -136,9 +134,9 @@
- + @if($role->name !== 'super-admin' && (auth()->user()->isSystemAdmin() || !$role->is_system))
@csrf @@ -170,134 +168,6 @@
- -
diff --git a/resources/views/layouts/partials/sidebar-menu.blade.php b/resources/views/layouts/partials/sidebar-menu.blade.php index ff22761..c6b47e3 100644 --- a/resources/views/layouts/partials/sidebar-menu.blade.php +++ b/resources/views/layouts/partials/sidebar-menu.blade.php @@ -189,8 +189,12 @@
  • {{ __('Product Management') }}
  • {{ __('Advertisement Management') }}
  • {{ __('Product Status') }}
  • + @can('menu.data-config.sub-accounts')
  • {{ __('Sub Accounts') }}
  • + @endcan + @can('menu.data-config.sub-account-roles')
  • + @endcan
  • {{ __('Point Settings') }}
  • {{ __('Badge Settings') }}
  • @@ -295,14 +299,18 @@
    @@ -320,18 +328,24 @@
    diff --git a/routes/web.php b/routes/web.php index 162d9af..c850b7a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -108,14 +108,16 @@ Route::middleware(['auth', 'verified', 'tenant.access'])->prefix('admin')->name( Route::get('/products', [App\Http\Controllers\Admin\DataConfigController::class , 'products'])->name('products'); Route::get('/advertisements', [App\Http\Controllers\Admin\DataConfigController::class , 'advertisements'])->name('advertisements'); Route::get('/admin-products', [App\Http\Controllers\Admin\DataConfigController::class , 'adminProducts'])->name('admin-products'); - Route::get('/sub-accounts', [App\Http\Controllers\Admin\PermissionController::class , 'accounts'])->name('sub-accounts'); - Route::post('/sub-accounts', [App\Http\Controllers\Admin\PermissionController::class , 'storeAccount'])->name('sub-accounts.store'); - Route::put('/sub-accounts/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'updateAccount'])->name('sub-accounts.update'); - Route::delete('/sub-accounts/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'destroyAccount'])->name('sub-accounts.destroy'); - Route::get('/sub-account-roles', [App\Http\Controllers\Admin\PermissionController::class , 'roles'])->name('sub-account-roles'); - Route::post('/sub-account-roles', [App\Http\Controllers\Admin\PermissionController::class , 'storeRole'])->name('sub-account-roles.store'); - Route::put('/sub-account-roles/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'updateRole'])->name('sub-account-roles.update'); - Route::delete('/sub-account-roles/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'destroyRole'])->name('sub-account-roles.destroy'); + Route::get('/sub-accounts', [App\Http\Controllers\Admin\PermissionController::class , 'accounts'])->name('sub-accounts')->middleware('can:menu.data-config.sub-accounts'); + Route::post('/sub-accounts', [App\Http\Controllers\Admin\PermissionController::class , 'storeAccount'])->name('sub-accounts.store')->middleware('can:menu.data-config.sub-accounts'); + Route::put('/sub-accounts/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'updateAccount'])->name('sub-accounts.update')->middleware('can:menu.data-config.sub-accounts'); + Route::delete('/sub-accounts/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'destroyAccount'])->name('sub-accounts.destroy')->middleware('can:menu.data-config.sub-accounts'); + Route::get('/sub-account-roles', [App\Http\Controllers\Admin\PermissionController::class , 'roles'])->name('sub-account-roles')->middleware('can:menu.data-config.sub-account-roles'); + Route::get('/sub-account-roles/create', [App\Http\Controllers\Admin\PermissionController::class , 'createRole'])->name('sub-account-roles.create')->middleware('can:menu.data-config.sub-account-roles'); + Route::get('/sub-account-roles/{id}/edit', [App\Http\Controllers\Admin\PermissionController::class , 'editRole'])->name('sub-account-roles.edit')->middleware('can:menu.data-config.sub-account-roles'); + Route::post('/sub-account-roles', [App\Http\Controllers\Admin\PermissionController::class , 'storeRole'])->name('sub-account-roles.store')->middleware('can:menu.data-config.sub-account-roles'); + Route::put('/sub-account-roles/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'updateRole'])->name('sub-account-roles.update')->middleware('can:menu.data-config.sub-account-roles'); + Route::delete('/sub-account-roles/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'destroyRole'])->name('sub-account-roles.destroy')->middleware('can:menu.data-config.sub-account-roles'); Route::get('/points', [App\Http\Controllers\Admin\DataConfigController::class , 'points'])->name('points'); Route::get('/badges', [App\Http\Controllers\Admin\DataConfigController::class , 'badges'])->name('badges'); } @@ -167,7 +169,7 @@ Route::middleware(['auth', 'verified', 'tenant.access'])->prefix('admin')->name( // 14. 基本設定 Route::prefix('basic-settings')->name('basic-settings.')->group(function () { // 機台設定 - Route::prefix('machines')->name('machines.')->group(function () { + Route::prefix('machines')->name('machines.')->middleware('can:menu.basic.machines')->group(function () { // 機台照片獨立更新 Route::patch('{machine}/photos', [App\Http\Controllers\Admin\BasicSettings\MachinePhotoController::class, 'update'])->name('photos.update'); @@ -178,7 +180,7 @@ Route::middleware(['auth', 'verified', 'tenant.access'])->prefix('admin')->name( }); // 客戶金流設定 - Route::resource('payment-configs', App\Http\Controllers\Admin\BasicSettings\PaymentConfigController::class)->except(['show']); + Route::resource('payment-configs', App\Http\Controllers\Admin\BasicSettings\PaymentConfigController::class)->except(['show'])->middleware('can:menu.basic.payment-configs'); // 機台型號設定 Route::resource('machine-models', App\Http\Controllers\Admin\BasicSettings\MachineModelController::class)->except(['show']); @@ -186,14 +188,16 @@ Route::middleware(['auth', 'verified', 'tenant.access'])->prefix('admin')->name( // 15. 權限設定 Route::prefix('permission')->name('permission.')->group(function () { - Route::resource('companies', App\Http\Controllers\Admin\CompanyController::class)->except(['show', 'create', 'edit']); - Route::get('/accounts', [App\Http\Controllers\Admin\PermissionController::class , 'accounts'])->name('accounts'); - Route::post('/accounts', [App\Http\Controllers\Admin\PermissionController::class , 'storeAccount'])->name('accounts.store'); - Route::put('/accounts/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'updateAccount'])->name('accounts.update'); - Route::delete('/accounts/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'destroyAccount'])->name('accounts.destroy'); - Route::get('/roles', [App\Http\Controllers\Admin\PermissionController::class , 'roles'])->name('roles'); - Route::post('/roles', [App\Http\Controllers\Admin\PermissionController::class , 'storeRole'])->name('roles.store'); - Route::put('/roles/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'updateRole'])->name('roles.update'); + Route::resource('companies', App\Http\Controllers\Admin\CompanyController::class)->except(['show', 'create', 'edit'])->middleware('can:menu.permissions.companies'); + Route::get('/accounts', [App\Http\Controllers\Admin\PermissionController::class , 'accounts'])->name('accounts')->middleware('can:menu.permissions.accounts'); + Route::post('/accounts', [App\Http\Controllers\Admin\PermissionController::class , 'storeAccount'])->name('accounts.store')->middleware('can:menu.permissions.accounts'); + Route::put('/accounts/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'updateAccount'])->name('accounts.update')->middleware('can:menu.permissions.accounts'); + Route::delete('/accounts/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'destroyAccount'])->name('accounts.destroy')->middleware('can:menu.permissions.accounts'); + Route::get('/roles', [App\Http\Controllers\Admin\PermissionController::class , 'roles'])->name('roles')->middleware('can:menu.permissions.roles'); + Route::get('/roles/create', [App\Http\Controllers\Admin\PermissionController::class , 'createRole'])->name('roles.create')->middleware('can:menu.permissions.roles'); + Route::get('/roles/{id}/edit', [App\Http\Controllers\Admin\PermissionController::class , 'editRole'])->name('roles.edit')->middleware('can:menu.permissions.roles'); + Route::post('/roles', [App\Http\Controllers\Admin\PermissionController::class , 'storeRole'])->name('roles.store')->middleware('can:menu.permissions.roles'); + Route::put('/roles/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'updateRole'])->name('roles.update')->middleware('can:menu.permissions.roles'); Route::delete('/roles/{id}', [App\Http\Controllers\Admin\PermissionController::class , 'destroyRole'])->name('roles.destroy'); } );