[FEAT] 角色權限編輯頁面重構與多項 UI/翻譯優化
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 44s
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 44s
- 新增獨立角色權限編輯頁面 (roles-edit.blade.php),採整合式佈局 - 重構 PermissionController 以支援角色建立/編輯/刪除完整 CRUD - 移除角色手動層級選擇,改為自動判定並顯示所屬單位 - 補齊 20+ 項 menu 權限 Key 的三語系翻譯 (zh_TW/en/ja) - 修正子項目佈局跑版問題 (min-w-0/flex-shrink-0 防溢出) - 更新 RoleSeeder 加入巢狀權限結構 - 同步更新側邊欄選單與路由配置
This commit is contained in:
@@ -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.'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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',
|
||||
|
||||
28
lang/en.json
28
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"
|
||||
}
|
||||
28
lang/ja.json
28
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": "公式"
|
||||
}
|
||||
@@ -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": "系統層"
|
||||
}
|
||||
308
resources/views/admin/permission/roles-edit.blade.php
Normal file
308
resources/views/admin/permission/roles-edit.blade.php
Normal file
@@ -0,0 +1,308 @@
|
||||
@extends('layouts.admin')
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6" x-data="{
|
||||
selectedPermissions: {{ json_encode($role->permissions->pluck('name')->toArray()) }},
|
||||
activeCategory: '',
|
||||
toggleCategory(category, permissions) {
|
||||
const allSelected = permissions.every(p => this.selectedPermissions.includes(p));
|
||||
if (allSelected) {
|
||||
this.selectedPermissions = this.selectedPermissions.filter(p => !permissions.includes(p));
|
||||
} else {
|
||||
const newPerms = permissions.filter(p => !this.selectedPermissions.includes(p));
|
||||
this.selectedPermissions = [...this.selectedPermissions, ...newPerms];
|
||||
}
|
||||
},
|
||||
isCategorySelected(permissions) {
|
||||
return permissions.length > 0 && permissions.every(p => this.selectedPermissions.includes(p));
|
||||
},
|
||||
isCategoryPartial(permissions) {
|
||||
const selectedCount = permissions.filter(p => this.selectedPermissions.includes(p)).length;
|
||||
return selectedCount > 0 && selectedCount < permissions.length;
|
||||
},
|
||||
scrollTo(id) {
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
window.scrollTo({
|
||||
top: el.offsetTop - 100,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
this.activeCategory = id;
|
||||
}
|
||||
}
|
||||
}">
|
||||
<!-- Header Section -->
|
||||
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
|
||||
<div class="flex items-center gap-4">
|
||||
<a href="{{ $back_url }}" class="inline-flex items-center justify-center p-2 rounded-xl bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 text-slate-500 hover:text-slate-900 dark:hover:text-white transition-all shadow-sm">
|
||||
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" /></svg>
|
||||
</a>
|
||||
<div>
|
||||
<h1 class="text-3xl font-black text-slate-800 dark:text-white tracking-tight font-display">
|
||||
{{ $title }}
|
||||
</h1>
|
||||
<div class="flex items-center gap-2 mt-1">
|
||||
<span class="px-2.5 py-0.5 rounded-lg bg-cyan-500/10 text-cyan-500 text-[10px] font-black tracking-widest uppercase">
|
||||
{{ $role->name ?: __('New Role') }}
|
||||
</span>
|
||||
@if($role->is_system)
|
||||
<span class="text-[10px] font-black text-emerald-500 uppercase tracking-widest">
|
||||
• {{ __('System Role') }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<button type="submit" form="role-permissions-form" class="btn-luxury-primary px-8 py-3">
|
||||
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>
|
||||
<span>{{ __('Save Changes') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div> @if(auth()->user()->roles->contains('id', $role->id))
|
||||
<div class="my-4 p-4 rounded-2xl bg-amber-500/5 border border-amber-500/20 flex items-center gap-4 animate-luxury-in ring-1 ring-amber-500/10">
|
||||
<div class="w-10 h-10 rounded-xl bg-amber-500/10 flex items-center justify-center flex-shrink-0 text-amber-500">
|
||||
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-amber-800 dark:text-amber-200 leading-tight">{{ __('Modifying your own administrative permissions may result in losing access to certain system functions.') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@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
|
||||
|
||||
<form id="role-permissions-form" action="{{ $action }}" method="POST" class="flex flex-col lg:flex-row gap-6 items-start">
|
||||
@csrf
|
||||
@if($method === 'PUT')
|
||||
@method('PUT')
|
||||
@endif
|
||||
|
||||
<!-- Sidebar: Control Panel -->
|
||||
<aside class="w-full lg:w-80 lg:sticky top-24 z-10 space-y-6">
|
||||
<!-- Role Info Card -->
|
||||
<div class="luxury-card p-6 rounded-3xl border border-slate-200/50 dark:border-slate-800/50 shadow-sm">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div class="w-10 h-10 rounded-2xl bg-cyan-500/10 text-cyan-500 flex items-center justify-center">
|
||||
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>
|
||||
</div>
|
||||
<h2 class="text-lg font-black text-slate-800 dark:text-white tracking-tight">{{ __('Role Identification') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="space-y-5">
|
||||
<div class="space-y-2">
|
||||
<label class="text-[11px] font-black text-slate-400 uppercase tracking-widest pl-1">{{ __('Role Name') }}</label>
|
||||
<input type="text" name="name" value="{{ old('name', $role->name) }}" required
|
||||
class="luxury-input w-full @error('name') border-rose-500 @enderror"
|
||||
placeholder="{{ __('Enter role name') }}"
|
||||
{{ $role->name === 'super-admin' ? 'readonly' : '' }}>
|
||||
@error('name')
|
||||
<p class="text-[11px] text-rose-500 font-bold mt-1.5 px-1">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="space-y-2 pt-2">
|
||||
<label class="text-[11px] font-black text-slate-400 uppercase tracking-widest pl-1">{{ __('Affiliated Unit') }}</label>
|
||||
<div class="px-4 py-3 rounded-2xl bg-slate-50 dark:bg-slate-800/50 border border-slate-200/50 dark:border-slate-700/50 flex items-center gap-3 group">
|
||||
<div class="w-8 h-8 rounded-xl bg-cyan-500/10 text-cyan-500 flex items-center justify-center">
|
||||
@if($role->is_system || (!$role->exists && auth()->user()->isSystemAdmin()))
|
||||
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>
|
||||
@else
|
||||
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" /></svg>
|
||||
@endif
|
||||
</div>
|
||||
<span class="text-sm font-black text-slate-700 dark:text-slate-200">
|
||||
@if($role->is_system || (!$role->exists && auth()->user()->isSystemAdmin()))
|
||||
{{ __('System Official') }}
|
||||
@else
|
||||
{{ $role->company->name ?? auth()->user()->company->name ?? '-' }}
|
||||
@endif
|
||||
</span>
|
||||
@if(auth()->user()->isSystemAdmin())
|
||||
<input type="hidden" name="is_system" value="{{ $role->exists ? ($role->is_system ? 1 : 0) : 1 }}">
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<!-- Sidebar: Stats Card -->
|
||||
<div class="luxury-card p-4 rounded-3xl border border-slate-200/50 dark:border-slate-800/50 shadow-sm overflow-hidden">
|
||||
<div class="flex items-center justify-between mb-4 pb-2 border-b border-slate-100 dark:border-slate-800">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-1">{{ __('Total Selected') }}</span>
|
||||
<div class="flex items-baseline gap-1">
|
||||
<span class="text-3xl font-display font-black text-cyan-500" x-text="selectedPermissions.length">0</span>
|
||||
<span class="text-xs font-bold text-slate-400">/ {{ $all_permissions->flatten()->count() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-2xl bg-emerald-500/5 flex items-center justify-center text-emerald-500">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Tip Box removed --}}
|
||||
</aside>
|
||||
|
||||
<!-- Main Content (Permissions Matrix) -->
|
||||
<div class="flex-1 w-full space-y-12">
|
||||
@foreach($all_permissions as $group => $permissions)
|
||||
@php
|
||||
$groupId = 'group-' . $group;
|
||||
$groupPermissions = $permissions->pluck('name')->toArray();
|
||||
@endphp
|
||||
<section id="{{ $groupId }}" class="luxury-card p-6 md:p-8 rounded-3xl border border-slate-200/50 dark:border-slate-800/50 shadow-sm animate-luxury-in scroll-mt-32">
|
||||
<div class="flex items-center justify-between mb-10 group/title">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="w-1.5 h-6 rounded-full bg-cyan-500"></span>
|
||||
<h2 class="text-xl font-black text-slate-800 dark:text-slate-100 tracking-tight whitespace-nowrap">
|
||||
{{ __($group == 'menu' ? 'Menu Permissions' : 'Other Permissions') }}
|
||||
</h2>
|
||||
</div>
|
||||
<label class="flex items-center gap-3 px-4 py-2 rounded-xl bg-slate-50 dark:bg-slate-800/50 cursor-pointer hover:bg-cyan-50 dark:hover:bg-cyan-500/10 transition-all border border-transparent hover:border-cyan-100 dark:hover:border-cyan-500/20 group">
|
||||
<span class="text-[11px] font-black text-slate-400 group-hover:text-cyan-600 uppercase tracking-widest transition-colors">{{ __('Select All') }}</span>
|
||||
<div class="relative flex items-center">
|
||||
<input type="checkbox"
|
||||
@change="toggleCategory('{{ $group }}', {{ json_encode($groupPermissions) }})"
|
||||
:checked="isCategorySelected({{ json_encode($groupPermissions) }})"
|
||||
:indeterminate="isCategoryPartial({{ json_encode($groupPermissions) }})"
|
||||
class="w-4 h-4 rounded border-2 border-slate-300 dark:border-slate-600 text-cyan-500 focus:ring-cyan-500/20 transition-all cursor-pointer accent-cyan-500">
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@if($group === 'menu')
|
||||
<div class="grid grid-cols-1 gap-8">
|
||||
@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
|
||||
|
||||
<div class="rounded-3xl border border-slate-100/80 dark:border-slate-800/80 overflow-hidden bg-slate-50/30 dark:bg-slate-900/20 hover:border-cyan-500/30 transition-all duration-300"
|
||||
x-data="{ expanded: true }">
|
||||
<div class="p-4 flex items-center gap-4">
|
||||
<div class="flex-1 flex items-center gap-3">
|
||||
@if($children->count() > 0)
|
||||
<button type="button" @click="expanded = !expanded" class="w-9 h-9 flex-shrink-0 rounded-xl bg-slate-50 dark:bg-slate-800/50 flex items-center justify-center text-slate-400 hover:text-cyan-500 dark:hover:text-cyan-400 transition-all border border-slate-100 dark:border-slate-800 hover:border-cyan-500/30">
|
||||
<svg class="w-4 h-4 transition-transform duration-300" :class="expanded ? 'rotate-180 text-cyan-500' : ''" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" /></svg>
|
||||
</button>
|
||||
@endif
|
||||
<div class="flex flex-col">
|
||||
<h3 class="text-base font-black text-slate-800 dark:text-white leading-tight tracking-tight">
|
||||
{{ __($parent->name) }}
|
||||
</h3>
|
||||
<span class="text-[9px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest font-mono mt-0.5">{{ $parent->name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center pr-1">
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<input type="checkbox" name="permissions[]" value="{{ $parent->name }}"
|
||||
x-model="selectedPermissions"
|
||||
class="sr-only peer">
|
||||
<div class="w-11 h-6 bg-slate-200 dark:bg-slate-700 rounded-full peer peer-focus:outline-none peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-cyan-500 transition-all duration-200 shadow-inner"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($children->count() > 0)
|
||||
<div x-show="expanded" x-collapse>
|
||||
<div class="px-8 pb-8 pt-2 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 bg-transparent">
|
||||
@foreach($children as $child)
|
||||
<label class="group relative flex items-center justify-between p-4 rounded-2xl border border-transparent bg-slate-100/50 dark:bg-slate-900/40 cursor-pointer transition-all hover:bg-cyan-50/50 dark:hover:bg-cyan-900/10 hover:shadow-sm hover:border-cyan-500/20">
|
||||
<div class="flex flex-col flex-1 min-w-0 mr-3">
|
||||
<span class="text-sm font-bold text-slate-500 dark:text-slate-400 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors break-words">{{ __($child->name) }}</span>
|
||||
<span class="text-[9px] font-bold text-slate-400 uppercase tracking-widest mt-0.5 truncate">{{ $child->name }}</span>
|
||||
</div>
|
||||
<div class="relative flex items-center flex-shrink-0">
|
||||
<input type="checkbox"
|
||||
name="permissions[]"
|
||||
value="{{ $child->id }}"
|
||||
class="w-4 h-4 rounded border-2 border-slate-300 dark:border-slate-700 text-cyan-500 focus:ring-cyan-500/20 transition-all cursor-pointer accent-cyan-500"
|
||||
{{ $role->hasPermissionTo($child->name) ? 'checked' : '' }}>
|
||||
</div>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
@foreach($permissions as $perm)
|
||||
<label class="group relative flex items-center justify-between p-4 rounded-2xl border border-slate-100/80 dark:border-slate-800/80 bg-slate-50/30 dark:bg-slate-900/20 hover:border-cyan-500/40 cursor-pointer transition-all duration-300">
|
||||
<div class="flex flex-col flex-1">
|
||||
<span class="text-sm font-black text-slate-500 dark:text-slate-400 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors">
|
||||
{{ __($perm->name) }}
|
||||
</span>
|
||||
<p class="text-[9px] font-bold text-slate-400 dark:text-slate-500 mt-1 uppercase tracking-widest font-mono">
|
||||
{{ $perm->name }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-shrink-0 pr-1">
|
||||
<input type="checkbox" name="permissions[]" value="{{ $perm->name }}"
|
||||
x-model="selectedPermissions"
|
||||
class="w-4 h-4 rounded border-2 border-slate-300 dark:border-slate-600 text-cyan-500 focus:ring-cyan-500/20 transition-all cursor-pointer accent-cyan-500">
|
||||
</div>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</section>
|
||||
@endforeach
|
||||
|
||||
<!-- Action Bar -->
|
||||
<div class="pt-8 flex items-center justify-end gap-4 border-t border-slate-100 dark:border-slate-800">
|
||||
<a href="{{ $back_url }}" class="btn-luxury-ghost px-8">{{ __('Cancel') }}</a>
|
||||
<button type="submit" class="btn-luxury-primary px-12 h-14 shadow-xl shadow-cyan-500/20">
|
||||
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>
|
||||
<span>{{ __('Save Permissions') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<style>
|
||||
[x-cloak] { display: none !important; }
|
||||
|
||||
.animate-luxury-in {
|
||||
animation: luxuryIn 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
@keyframes luxuryIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
@@ -43,14 +43,12 @@
|
||||
<h1 class="text-3xl font-black text-slate-800 dark:text-white font-display tracking-tight">{{ $title }}</h1>
|
||||
<p class="text-sm font-bold text-slate-500 dark:text-slate-400 mt-1 uppercase tracking-widest">{{ __('Define and manage security roles and permissions.') }}</p>
|
||||
</div>
|
||||
<button @click="openModal()" class="btn-luxury-primary text-sm">
|
||||
<a href="{{ route($baseRoute . '.create') }}" class="btn-luxury-primary text-sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
||||
<span>{{ __('Add Role') }}</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Roles Content (Integrated Card) -->
|
||||
<div class="luxury-card rounded-3xl p-8 animate-luxury-in">
|
||||
<!-- Toolbar -->
|
||||
@@ -119,7 +117,7 @@
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-6">
|
||||
<td class="px-6 py-6" width="30%">
|
||||
<div class="flex flex-wrap gap-1 max-w-xs">
|
||||
@forelse($role->permissions->take(6) as $permission)
|
||||
<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">{{ __(str_replace('menu.', '', $permission->name)) }}</span>
|
||||
@@ -136,9 +134,9 @@
|
||||
</td>
|
||||
<td class="px-6 py-6 text-right">
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<button @click="openModal(true, @js($role->id), @js($role->name), @js($role->permissions->pluck('name')), {{ $role->is_system ? 'true' : 'false' }})" class="p-2 rounded-lg bg-slate-50 dark:bg-slate-800 text-slate-400 hover:text-cyan-500 hover:bg-cyan-500/5 transition-all border border-transparent hover:border-cyan-500/20 tooltip" title="{{ __('Edit') }}">
|
||||
<a href="{{ route($baseRoute . '.edit', $role->id) }}" class="p-2 rounded-lg bg-slate-50 dark:bg-slate-800 text-slate-400 hover:text-cyan-500 hover:bg-cyan-500/5 transition-all border border-transparent hover:border-cyan-500/20 tooltip" title="{{ __('Edit') }}">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"/></svg>
|
||||
</button>
|
||||
</a>
|
||||
@if($role->name !== 'super-admin' && (auth()->user()->isSystemAdmin() || !$role->is_system))
|
||||
<form action="{{ route($baseRoute . '.destroy', $role->id) }}" method="POST" @submit.prevent="if({{ $role->users()->count() }} > 0) { triggerDeleteWarning('{{ __('Cannot delete role with active users.') }}'); return; } confirmDelete('{{ route($baseRoute . '.destroy', $role->id) }}')" class="inline text-slate-400">
|
||||
@csrf
|
||||
@@ -170,134 +168,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<template x-if="showModal">
|
||||
<div class="fixed inset-0 z-[60] flex items-center justify-center p-4">
|
||||
<div @click="showModal = false" class="absolute inset-0 bg-slate-900/60 backdrop-blur-sm transition-opacity"></div>
|
||||
|
||||
<div class="relative w-full max-w-3xl bg-white dark:bg-slate-900 rounded-3xl shadow-2xl overflow-hidden animate-luxury-in">
|
||||
<div class="flex items-center justify-between p-8 border-b border-slate-100 dark:border-slate-800">
|
||||
<div>
|
||||
<h3 class="text-2xl font-black text-slate-800 dark:text-white" x-text="modalTitle"></h3>
|
||||
<p class="text-sm font-bold text-slate-400 mt-1" x-text="isEdit ? '{{ __('Update existing role and permissions.') }}' : '{{ __('Create a new role and assign permissions.') }}'"></p>
|
||||
</div>
|
||||
<button @click="showModal = false" class="p-2 rounded-xl hover:bg-slate-100 dark:hover:bg-slate-800 text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 transition-all">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form :action="isEdit ? '{{ route($baseRoute) }}/' + roleId : '{{ route($baseRoute . '.store') }}'" method="POST">
|
||||
@csrf
|
||||
<template x-if="isEdit"><input type="hidden" name="_method" value="PUT"></template>
|
||||
<input type="hidden" name="roleId" x-model="roleId">
|
||||
|
||||
<div class="p-8 max-h-[65vh] overflow-y-auto custom-scrollbar">
|
||||
<!-- Warning for editing own role -->
|
||||
<template x-if="isEdit && (currentUserRoleIds || []).map(String).includes(String(roleId))">
|
||||
<div class="mb-8 p-5 bg-amber-500/10 border border-amber-500/20 text-amber-600 dark:text-amber-400 rounded-2xl font-bold flex items-start gap-4 animate-luxury-in">
|
||||
<div class="size-10 bg-amber-500/20 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<p class="text-base font-black leading-tight">{{ __('Warning: You are editing your own role!') }}</p>
|
||||
<p class="font-bold mt-1 opacity-90 leading-relaxed">{{ __('Modifying your own administrative permissions may result in losing access to certain system functions.') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<!-- Left: Basic Info -->
|
||||
<div class="space-y-6">
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-400 uppercase tracking-widest pl-1">{{ __('Role Name') }}</label>
|
||||
<input type="text" name="name" x-model="roleName" required class="luxury-input w-full @error('name') border-rose-500 @enderror" placeholder="{{ __('Enter role name') }}" :disabled="isEdit && roleName === 'super-admin'">
|
||||
@error('name')
|
||||
<p class="text-[11px] text-rose-500 font-bold mt-1.5 px-1 flex items-center gap-1.5 animate-luxury-in">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
|
||||
{{ $message }}
|
||||
</p>
|
||||
@enderror
|
||||
<template x-if="isEdit && roleName === 'super-admin'">
|
||||
<p class="text-[10px] text-amber-500 font-bold mt-1 px-1 flex items-center gap-1.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
|
||||
{{ __('The Super Admin role name cannot be modified.') }}
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@if(auth()->user()->isSystemAdmin())
|
||||
<div class="space-y-4">
|
||||
<label class="text-xs font-black text-slate-400 uppercase tracking-widest pl-1">{{ __('Affiliation') }}</label>
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="flex items-center gap-3 p-3 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800/50 cursor-pointer transition-all border border-slate-200 dark:border-slate-800 group has-[:checked]:border-cyan-500/50 has-[:checked]:bg-cyan-500/5">
|
||||
<input type="radio" name="is_system" value="1" x-model="isSystem" class="w-4 h-4 text-cyan-500 bg-transparent border-slate-300 focus:ring-cyan-500" :disabled="isEdit && roleName === 'super-admin'">
|
||||
<span class="text-sm font-bold text-slate-700 dark:text-slate-200 group-hover:text-cyan-600 dark:group-hover:text-cyan-400">{{ __('System Level') }}</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-3 p-3 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800/50 cursor-pointer transition-all border border-slate-200 dark:border-slate-800 group has-[:checked]:border-cyan-500/50 has-[:checked]:bg-cyan-500/5">
|
||||
<input type="radio" name="is_system" value="0" x-model="isSystem" class="w-4 h-4 text-cyan-500 bg-transparent border-slate-300 focus:ring-cyan-500" :disabled="isEdit && roleName === 'super-admin'">
|
||||
<span class="text-sm font-bold text-slate-700 dark:text-slate-200 group-hover:text-cyan-600 dark:group-hover:text-cyan-400">{{ __('Company Level') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Right: Permissions -->
|
||||
<div class="space-y-6">
|
||||
<label class="text-xs font-black text-slate-400 uppercase tracking-widest pl-1">{{ __('Menu Permissions') }}</label>
|
||||
<div class="luxury-card border-slate-100 dark:border-slate-800 p-4 rounded-2xl grid grid-cols-1 gap-3">
|
||||
@php
|
||||
$icon_map = [
|
||||
'members' => 'M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z',
|
||||
'machines' => 'M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 01-2 2v4a2 2 0 012 2h14a2 2 0 012-2v-4a2 2 0 01-2-2m-2-4h.01M17 16h.01',
|
||||
'app' => 'M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z',
|
||||
'warehouses' => 'M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10',
|
||||
'sales' => 'M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
|
||||
'analysis' => 'M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4h16v12a1 1 0 01-1 1H5a1 1 0 01-1-1V4z',
|
||||
'audit' => 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z',
|
||||
'data-config' => 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z',
|
||||
'remote' => 'M5.636 18.364a9 9 0 010-12.728m12.728 0a9 9 0 010 12.728m-9.9-2.829a5 5 0 010-7.07m7.072 0a5 5 0 010 7.07M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
|
||||
'line' => 'M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z',
|
||||
'reservation' => 'M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z',
|
||||
'special-permission' => 'M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z',
|
||||
'basic-settings' => 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z',
|
||||
'permissions' => 'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z',
|
||||
];
|
||||
@endphp
|
||||
@foreach($all_permissions->get('menu', []) as $permission)
|
||||
@php
|
||||
$pure_name = str_replace('menu.', '', $permission->name);
|
||||
$is_restricted = in_array($permission->name, ['menu.basic-settings', 'menu.permissions']);
|
||||
@endphp
|
||||
<label class="flex items-center gap-3 p-3 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800/50 cursor-pointer transition-all border border-transparent hover:border-slate-200 dark:hover:border-slate-700 group"
|
||||
@if($is_restricted) x-show="isSystem == '1'" x-transition @endif>
|
||||
<div class="relative flex items-center">
|
||||
<input type="checkbox" name="permissions[]" value="{{ $permission->name }}"
|
||||
x-model="rolePermissions"
|
||||
class="peer w-5 h-5 rounded-lg border-2 border-slate-200 dark:border-slate-700 text-cyan-500 focus:ring-cyan-500 focus:ring-offset-0 bg-transparent transition-all checked:border-cyan-500">
|
||||
<svg class="absolute w-3.5 h-3.5 text-white opacity-0 peer-checked:opacity-100 left-0.5 pointer-events-none transition-opacity" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
</div>
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="w-7 h-7 rounded-lg flex items-center justify-center bg-slate-50 dark:bg-slate-800 text-slate-400 group-hover:bg-cyan-500 group-hover:text-white transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="{{ $icon_map[$pure_name] ?? 'M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z' }}" /></svg>
|
||||
</div>
|
||||
<span class="text-sm font-bold text-slate-600 dark:text-slate-300 group-hover:text-slate-900 dark:group-hover:text-white tracking-wide transition-colors">{{ __($pure_name) }}</span>
|
||||
</div>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-8 bg-slate-50 dark:bg-slate-800/50 border-t border-slate-100 dark:border-slate-800 flex items-center justify-end gap-4">
|
||||
<button type="button" @click="showModal = false" class="btn-luxury-ghost px-8 py-3 rounded-2xl font-black text-xs uppercase tracking-widest">{{ __('Cancel') }}</button>
|
||||
<button type="submit" class="btn-luxury-primary px-10 py-3 rounded-2xl font-black text-xs uppercase tracking-widest shadow-xl shadow-cyan-500/20">{{ __('Save Changes') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- Global Delete Warning Modal -->
|
||||
<div x-show="isWarningModalOpen" class="fixed inset-0 z-[200] 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">
|
||||
|
||||
@@ -189,8 +189,12 @@
|
||||
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.data-config.products') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.data-config.products') }}">{{ __('Product Management') }}</a></li>
|
||||
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.data-config.advertisements') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.data-config.advertisements') }}">{{ __('Advertisement Management') }}</a></li>
|
||||
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.data-config.admin-products') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.data-config.admin-products') }}">{{ __('Product Status') }}</a></li>
|
||||
@can('menu.data-config.sub-accounts')
|
||||
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.data-config.sub-accounts') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.data-config.sub-accounts') }}">{{ __('Sub Accounts') }}</a></li>
|
||||
@endcan
|
||||
@can('menu.data-config.sub-account-roles')
|
||||
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.data-config.sub-account-roles') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.data-config.sub-account-roles') }}">{{ __('Sub Account Roles') }}</a></li>
|
||||
@endcan
|
||||
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.data-config.points') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.data-config.points') }}">{{ __('Point Settings') }}</a></li>
|
||||
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.data-config.badges') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.data-config.badges') }}">{{ __('Badge Settings') }}</a></li>
|
||||
</ul>
|
||||
@@ -295,14 +299,18 @@
|
||||
</button>
|
||||
<div x-show="open" x-collapse>
|
||||
<ul class="luxury-submenu" data-sidebar-sub>
|
||||
@can('menu.basic.machines')
|
||||
<li><a class="flex items-center gap-x-3 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.basic-settings.machines.*') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.basic-settings.machines.index') }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 shrink-0 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" /></svg>
|
||||
{{ __('Machine Settings') }}
|
||||
</a></li>
|
||||
@endcan
|
||||
@can('menu.basic.payment-configs')
|
||||
<li><a class="flex items-center gap-x-3 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.basic-settings.payment-configs.*') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.basic-settings.payment-configs.index') }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 shrink-0 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" /></svg>
|
||||
{{ __('Customer Payment Config') }}
|
||||
</a></li>
|
||||
@endcan
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
@@ -320,18 +328,24 @@
|
||||
</button>
|
||||
<div x-show="open" x-collapse>
|
||||
<ul class="luxury-submenu" data-sidebar-sub>
|
||||
@can('menu.permissions.companies')
|
||||
<li><a class="flex items-center gap-x-3 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.permission.companies.*') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.permission.companies.index') }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 shrink-0 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" /></svg>
|
||||
{{ __('Customer Management') }}
|
||||
</a></li>
|
||||
@endcan
|
||||
@can('menu.permissions.accounts')
|
||||
<li><a class="flex items-center gap-x-3 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.permission.accounts') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.permission.accounts') }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 shrink-0 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
||||
{{ __('Account Management') }}
|
||||
</a></li>
|
||||
@endcan
|
||||
@can('menu.permissions.roles')
|
||||
<li><a class="flex items-center gap-x-3 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.permission.roles') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.permission.roles') }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 shrink-0 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>
|
||||
{{ __('Role Permissions') }}
|
||||
</a></li>
|
||||
@endcan
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user