RolePermission ve UserPermission css düzenlemesi
This commit is contained in:
parent
6177dbcf24
commit
08c495943b
2 changed files with 277 additions and 70 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import { FaChevronRight, FaChevronDown } from 'react-icons/fa'
|
||||
import Container from '@/components/shared/Container'
|
||||
import { Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
|
||||
import { useConfig } from '@/components/ui/ConfigProvider'
|
||||
|
|
@ -8,13 +9,14 @@ import {
|
|||
PermissionGroupDto,
|
||||
PermissionWithGroupName,
|
||||
PermissionWithStyle,
|
||||
UpdatePermissionDto,
|
||||
} from '@/proxy/admin/models'
|
||||
import { getPermissions, updatePermissions } from '@/services/identity.service'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { ChangeEvent, useEffect, useMemo, useState } from 'react'
|
||||
|
||||
type OpenState = Record<string, boolean>
|
||||
|
||||
function RolesPermission({
|
||||
open,
|
||||
onDialogClose,
|
||||
|
|
@ -24,6 +26,24 @@ function RolesPermission({
|
|||
onDialogClose: () => void
|
||||
name: string
|
||||
}) {
|
||||
// Collapse/expand state
|
||||
const [openPermissions, setOpenPermissions] = useState<OpenState>({})
|
||||
|
||||
// Parent izinleri aç/kapat
|
||||
const togglePermission = (permName: string) => {
|
||||
setOpenPermissions((prev) => ({ ...prev, [permName]: !prev[permName] }))
|
||||
}
|
||||
|
||||
// Parent izin mi?
|
||||
function isParent(permission: PermissionWithStyle) {
|
||||
return selectedGroupPermissions.some((p) => p.parentName === permission.name)
|
||||
}
|
||||
|
||||
// Belirli parent'ın alt izinleri
|
||||
function getChildren(parentName: string) {
|
||||
if (!parentName) return []
|
||||
return selectedGroupPermissions.filter((p) => p.parentName === parentName)
|
||||
}
|
||||
const providerName = 'R'
|
||||
const { translate } = useLocalization()
|
||||
const { getConfig } = useStoreActions((a) => a.abpConfig)
|
||||
|
|
@ -135,6 +155,28 @@ function RolesPermission({
|
|||
}
|
||||
}
|
||||
|
||||
// --- Expand/Collapse All fonksiyonları (tüm state ve yardımcı fonksiyonlardan sonra) ---
|
||||
const handleExpandAll = () => {
|
||||
const expanded: OpenState = {}
|
||||
function expandRecursively(perms: PermissionWithStyle[]) {
|
||||
perms.forEach((perm) => {
|
||||
if (isParent(perm)) {
|
||||
expanded[perm.name || ''] = true
|
||||
// Alt parent'ları da recursive aç
|
||||
const children = getChildren(perm.name || '')
|
||||
expandRecursively(children)
|
||||
}
|
||||
})
|
||||
}
|
||||
// Tüm parent ve alt parent'ları recursive olarak aç
|
||||
expandRecursively(selectedGroupPermissions)
|
||||
setOpenPermissions(expanded)
|
||||
}
|
||||
|
||||
const handleCollapseAll = () => {
|
||||
setOpenPermissions({})
|
||||
}
|
||||
|
||||
function getPermissionsWithGroupName(groups: PermissionGroupDto[]): PermissionWithGroupName[] {
|
||||
return groups.reduce(
|
||||
(acc, val) => [
|
||||
|
|
@ -174,34 +216,43 @@ function RolesPermission({
|
|||
[selectedGroupPermissions],
|
||||
)
|
||||
|
||||
// Collapse/expand için recursive düzende izinleri döndür
|
||||
const filteredPermissions = useMemo(() => {
|
||||
if (!searchTerm) {
|
||||
return selectedGroupPermissions
|
||||
}
|
||||
|
||||
const lowerTerm = searchTerm.toLowerCase()
|
||||
|
||||
// 1️⃣ Sadece 1. kırınımdakileri (parent olmayanları değil) ara
|
||||
const topLevelMatches = selectedGroupPermissions.filter(
|
||||
// Filtreli veya tüm parent izinler
|
||||
const parents = selectedGroupPermissions.filter(
|
||||
(p) =>
|
||||
!p.parentName && // parentName yoksa bu 1. kırınım demektir
|
||||
translate('::' + p.displayName)
|
||||
.toLowerCase()
|
||||
.includes(lowerTerm),
|
||||
!p.parentName &&
|
||||
(searchTerm === '' ||
|
||||
translate('::' + p.displayName)
|
||||
.toLowerCase()
|
||||
.includes(lowerTerm)),
|
||||
)
|
||||
|
||||
// 2️⃣ Her bulunan parent’ın altındaki child izinleri ekle
|
||||
const result: PermissionWithStyle[] = []
|
||||
topLevelMatches.forEach((parent) => {
|
||||
result.push(parent)
|
||||
// Recursive children builder (her seviye için doğru level)
|
||||
function buildTree(
|
||||
parent: PermissionWithStyle,
|
||||
level: number,
|
||||
): (PermissionWithStyle & { level: number })[] {
|
||||
const arr = [{ ...parent, level }]
|
||||
const parentKey = parent.name || ''
|
||||
if (openPermissions[parentKey] || searchTerm) {
|
||||
const children = getChildren(parentKey)
|
||||
children.forEach((child) => {
|
||||
arr.push(...buildTree(child, level + 1))
|
||||
})
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// Alt kırılımları bul
|
||||
const children = selectedGroupPermissions.filter((child) => child.parentName === parent.name)
|
||||
result.push(...children)
|
||||
// Tüm parentlar ve altlarını sırayla ekle
|
||||
let result: (PermissionWithStyle & { level: number })[] = []
|
||||
parents.forEach((parent) => {
|
||||
result = result.concat(buildTree(parent, 0))
|
||||
})
|
||||
|
||||
return result
|
||||
}, [selectedGroupPermissions, searchTerm, translate])
|
||||
}, [selectedGroupPermissions, searchTerm, translate, openPermissions])
|
||||
|
||||
const onSelectAll = (value: boolean, e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!permissionList) {
|
||||
|
|
@ -284,6 +335,14 @@ function RolesPermission({
|
|||
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
|
||||
{translate('AbpPermissionManagement::SelectAllInThisTab')}
|
||||
</Checkbox>
|
||||
|
||||
|
||||
<Button size="sm" variant="plain" onClick={handleExpandAll} icon={<FaChevronRight />}>
|
||||
{translate('::ListForms.ListFormEdit.ExpandAll')}
|
||||
</Button>
|
||||
<Button size="sm" variant="plain" onClick={handleCollapseAll} icon={<FaChevronDown />}>
|
||||
{translate('::ListForms.ListFormEdit.CollapseAll')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -310,26 +369,56 @@ function RolesPermission({
|
|||
</div>
|
||||
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
|
||||
<hr className="mb-2"></hr>
|
||||
<Input
|
||||
size="sm"
|
||||
className="mb-2"
|
||||
placeholder={translate('::Search')}
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Input
|
||||
size="sm"
|
||||
className=""
|
||||
placeholder={translate('::Search')}
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="my-1">
|
||||
{filteredPermissions.map((permission) => (
|
||||
<div key={permission.name}>
|
||||
<Checkbox
|
||||
name={permission.name}
|
||||
className={permission.class}
|
||||
checked={permission.isGranted}
|
||||
onChange={() => onClickCheckbox(permission)}
|
||||
>
|
||||
{translate('::' + permission.displayName)}
|
||||
</Checkbox>
|
||||
</div>
|
||||
))}
|
||||
{filteredPermissions.map((permission) => {
|
||||
const isParentPerm = isParent(permission)
|
||||
const permKey = permission.name || ''
|
||||
const children = getChildren(permKey)
|
||||
return (
|
||||
<div key={permission.name} className={`ml-${permission.level * 4} group`}>
|
||||
<div className="flex items-center gap-2 px-2 py-0.5 rounded-md hover:bg-gray-50 transition-all">
|
||||
{/* Expand Icon */}
|
||||
{isParentPerm ? (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
togglePermission(permKey)
|
||||
}}
|
||||
className="w-5 h-5 flex items-center justify-center rounded hover:bg-gray-200 transition"
|
||||
>
|
||||
{openPermissions[permKey] || searchTerm ? (
|
||||
<FaChevronDown className="text-gray-500 text-xs" />
|
||||
) : (
|
||||
<FaChevronRight className="text-gray-500 text-xs" />
|
||||
)}
|
||||
</button>
|
||||
) : (
|
||||
<div className="w-5" />
|
||||
)}
|
||||
|
||||
{/* Checkbox */}
|
||||
<Checkbox
|
||||
name={permission.name}
|
||||
checked={permission.isGranted}
|
||||
onChange={() => onClickCheckbox(permission)}
|
||||
>
|
||||
<span className="text-sm text-gray-700 group-hover:text-gray-900 transition">
|
||||
{translate('::' + permission.displayName)}
|
||||
</span>
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import AdaptableCard from '@/components/shared/AdaptableCard'
|
||||
import Container from '@/components/shared/Container'
|
||||
import { Badge, Button, Checkbox, Dialog, Menu, toast } from '@/components/ui'
|
||||
import { FaChevronRight, FaChevronDown } from 'react-icons/fa'
|
||||
import { Badge, Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
|
||||
type OpenState = Record<string, boolean>
|
||||
import { useConfig } from '@/components/ui/ConfigProvider'
|
||||
import Notification from '@/components/ui/Notification'
|
||||
import {
|
||||
|
|
@ -10,7 +10,6 @@ import {
|
|||
PermissionWithGroupName,
|
||||
PermissionWithStyle,
|
||||
ProviderInfoDto,
|
||||
UpdatePermissionDto,
|
||||
} from '@/proxy/admin/models'
|
||||
import { getPermissions, updatePermissions } from '@/services/identity.service'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
|
|
@ -39,6 +38,44 @@ function UsersPermission({
|
|||
[],
|
||||
)
|
||||
|
||||
// Collapse/expand state
|
||||
const [openPermissions, setOpenPermissions] = useState<OpenState>({})
|
||||
|
||||
// Parent izinleri aç/kapat
|
||||
const togglePermission = (permName: string) => {
|
||||
setOpenPermissions((prev) => ({ ...prev, [permName]: !prev[permName] }))
|
||||
}
|
||||
|
||||
// Parent izin mi?
|
||||
function isParent(permission: PermissionWithStyle) {
|
||||
return selectedGroupPermissions.some((p) => p.parentName === permission.name)
|
||||
}
|
||||
|
||||
// Belirli parent'ın alt izinleri
|
||||
function getChildren(parentName: string) {
|
||||
if (!parentName) return []
|
||||
return selectedGroupPermissions.filter((p) => p.parentName === parentName)
|
||||
}
|
||||
// --- Expand/Collapse All fonksiyonları ---
|
||||
const handleExpandAll = () => {
|
||||
const expanded: OpenState = {}
|
||||
function expandRecursively(perms: PermissionWithStyle[]) {
|
||||
perms.forEach((perm) => {
|
||||
if (isParent(perm)) {
|
||||
expanded[perm.name || ''] = true
|
||||
const children = getChildren(perm.name || '')
|
||||
expandRecursively(children)
|
||||
}
|
||||
})
|
||||
}
|
||||
expandRecursively(selectedGroupPermissions)
|
||||
setOpenPermissions(expanded)
|
||||
}
|
||||
|
||||
const handleCollapseAll = () => {
|
||||
setOpenPermissions({})
|
||||
}
|
||||
|
||||
const mode = useStoreState((state) => state.theme.mode)
|
||||
|
||||
const { direction } = useConfig()
|
||||
|
|
@ -216,6 +253,42 @@ function UsersPermission({
|
|||
return false
|
||||
}
|
||||
|
||||
// --- Search ve recursive treeview ---
|
||||
// Collapse/expand için recursive düzende izinleri döndür
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const filteredPermissions = useMemo(() => {
|
||||
const lowerTerm = searchTerm.toLowerCase()
|
||||
// Filtreli veya tüm parent izinler
|
||||
const parents = selectedGroupPermissions.filter(
|
||||
(p) =>
|
||||
!p.parentName &&
|
||||
(searchTerm === '' ||
|
||||
translate('::' + p.displayName)
|
||||
.toLowerCase()
|
||||
.includes(lowerTerm)),
|
||||
)
|
||||
// Recursive children builder (her seviye için doğru level)
|
||||
function buildTree(
|
||||
parent: PermissionWithStyle,
|
||||
level: number,
|
||||
): (PermissionWithStyle & { level: number })[] {
|
||||
const arr = [{ ...parent, level }]
|
||||
const parentKey = parent.name || ''
|
||||
if (openPermissions[parentKey] || searchTerm) {
|
||||
const children = getChildren(parentKey)
|
||||
children.forEach((child) => {
|
||||
arr.push(...buildTree(child, level + 1))
|
||||
})
|
||||
}
|
||||
return arr
|
||||
}
|
||||
let result: (PermissionWithStyle & { level: number })[] = []
|
||||
parents.forEach((parent) => {
|
||||
result = result.concat(buildTree(parent, 0))
|
||||
})
|
||||
return result
|
||||
}, [selectedGroupPermissions, searchTerm, translate, openPermissions])
|
||||
|
||||
return permissionList ? (
|
||||
<Dialog
|
||||
width="min(900px, 95vw)"
|
||||
|
|
@ -224,7 +297,7 @@ function UsersPermission({
|
|||
onClose={onDialogClose}
|
||||
onRequestClose={onDialogClose}
|
||||
>
|
||||
<h5 className="mb-4">
|
||||
<h5 className="mb-1">
|
||||
{translate('::Permission')} - {name}
|
||||
</h5>
|
||||
<hr className="mt-1 mb-2"></hr>
|
||||
|
|
@ -239,12 +312,18 @@ function UsersPermission({
|
|||
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
|
||||
{translate('AbpPermissionManagement::SelectAllInThisTab')}
|
||||
</Checkbox>
|
||||
<Button size="sm" variant="plain" onClick={handleExpandAll} icon={<FaChevronRight />}>
|
||||
{translate('::ListForms.ListFormEdit.ExpandAll')}
|
||||
</Button>
|
||||
<Button size="sm" variant="plain" onClick={handleCollapseAll} icon={<FaChevronDown />}>
|
||||
{translate('::ListForms.ListFormEdit.CollapseAll')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<div className="w-full md:w-1/3 max-h-[450px] overflow-y-auto">
|
||||
<hr className="mt-2 mb-2"></hr>
|
||||
<hr className="mb-2"></hr>
|
||||
<Menu variant={mode} defaultActiveKeys={[selectedGroup?.displayName ?? '']}>
|
||||
{permissionList?.groups.map((group) => (
|
||||
<Menu.MenuItem
|
||||
|
|
@ -262,31 +341,70 @@ function UsersPermission({
|
|||
</Menu>
|
||||
</div>
|
||||
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
|
||||
<hr className="mt-2 mb-2"></hr>
|
||||
<div className="card-body">
|
||||
{selectedGroupPermissions.map((permission) => (
|
||||
<div key={permission.name}>
|
||||
<Checkbox
|
||||
name={permission.name}
|
||||
className={permission.class}
|
||||
disabled={isGrantedByOtherProviderName(permission.grantedProviders)}
|
||||
//disabled={permission.grantedProviders.length > 0 ? true : false}
|
||||
checked={permission.isGranted}
|
||||
onChange={() => onClickCheckbox(permission)}
|
||||
>
|
||||
{translate('::' + permission.displayName)}
|
||||
{permission.grantedProviders.map((provider) => (
|
||||
<Badge
|
||||
key={provider.providerKey}
|
||||
className="m-2"
|
||||
content={`${provider.providerName}${
|
||||
provider.providerName !== providerName ? `: ${provider.providerKey}` : ''
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</Checkbox>
|
||||
</div>
|
||||
))}
|
||||
<hr className="mb-2"></hr>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Input
|
||||
size="sm"
|
||||
className=""
|
||||
placeholder={translate('::Search')}
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="my-1">
|
||||
{filteredPermissions.map((permission) => {
|
||||
const isParentPerm = isParent(permission)
|
||||
const permKey = permission.name || ''
|
||||
const children = getChildren(permKey)
|
||||
return (
|
||||
<div key={permission.name} className={`ml-${permission.level * 4} group`}>
|
||||
<div className="flex items-center gap-2 px-2 py-0.5 rounded-md hover:bg-gray-50 transition-all">
|
||||
{/* Expand Icon */}
|
||||
{isParentPerm ? (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
togglePermission(permKey)
|
||||
}}
|
||||
className="w-5 h-5 flex items-center justify-center rounded hover:bg-gray-200 transition"
|
||||
>
|
||||
{openPermissions[permKey] || searchTerm ? (
|
||||
<FaChevronDown className="text-gray-500 text-xs" />
|
||||
) : (
|
||||
<FaChevronRight className="text-gray-500 text-xs" />
|
||||
)}
|
||||
</button>
|
||||
) : (
|
||||
<div className="w-5" />
|
||||
)}
|
||||
|
||||
{/* Checkbox */}
|
||||
<Checkbox
|
||||
name={permission.name}
|
||||
checked={permission.isGranted}
|
||||
onChange={() => onClickCheckbox(permission)}
|
||||
disabled={permission.grantedProviders.some((provider) => provider.providerName === 'R')}
|
||||
>
|
||||
<span className="text-sm text-gray-700 group-hover:text-gray-900 transition">
|
||||
{translate('::' + permission.displayName)}
|
||||
{permission.grantedProviders.map((provider) => {
|
||||
const badgeContent = provider.providerName !== providerName
|
||||
? `${provider.providerName}: ${provider.providerKey}`
|
||||
: provider.providerName;
|
||||
return (
|
||||
<Badge
|
||||
key={provider.providerKey}
|
||||
className="m-2"
|
||||
content={badgeContent}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue