RolePermission ve UserPermission css düzenlemesi

This commit is contained in:
Sedat Öztürk 2026-03-29 15:30:57 +03:00
parent 6177dbcf24
commit 08c495943b
2 changed files with 277 additions and 70 deletions

View file

@ -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>

View file

@ -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>