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 Container from '@/components/shared/Container'
import { Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui' import { Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
import { useConfig } from '@/components/ui/ConfigProvider' import { useConfig } from '@/components/ui/ConfigProvider'
@ -8,13 +9,14 @@ import {
PermissionGroupDto, PermissionGroupDto,
PermissionWithGroupName, PermissionWithGroupName,
PermissionWithStyle, PermissionWithStyle,
UpdatePermissionDto,
} from '@/proxy/admin/models' } from '@/proxy/admin/models'
import { getPermissions, updatePermissions } from '@/services/identity.service' import { getPermissions, updatePermissions } from '@/services/identity.service'
import { useStoreActions, useStoreState } from '@/store' import { useStoreActions, useStoreState } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { ChangeEvent, useEffect, useMemo, useState } from 'react' import { ChangeEvent, useEffect, useMemo, useState } from 'react'
type OpenState = Record<string, boolean>
function RolesPermission({ function RolesPermission({
open, open,
onDialogClose, onDialogClose,
@ -24,6 +26,24 @@ function RolesPermission({
onDialogClose: () => void onDialogClose: () => void
name: string 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 providerName = 'R'
const { translate } = useLocalization() const { translate } = useLocalization()
const { getConfig } = useStoreActions((a) => a.abpConfig) 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[] { function getPermissionsWithGroupName(groups: PermissionGroupDto[]): PermissionWithGroupName[] {
return groups.reduce( return groups.reduce(
(acc, val) => [ (acc, val) => [
@ -174,34 +216,43 @@ function RolesPermission({
[selectedGroupPermissions], [selectedGroupPermissions],
) )
// Collapse/expand için recursive düzende izinleri döndür
const filteredPermissions = useMemo(() => { const filteredPermissions = useMemo(() => {
if (!searchTerm) {
return selectedGroupPermissions
}
const lowerTerm = searchTerm.toLowerCase() const lowerTerm = searchTerm.toLowerCase()
// 1⃣ Sadece 1. kırınımdakileri (parent olmayanları değil) ara // Filtreli veya tüm parent izinler
const topLevelMatches = selectedGroupPermissions.filter( const parents = selectedGroupPermissions.filter(
(p) => (p) =>
!p.parentName && // parentName yoksa bu 1. kırınım demektir !p.parentName &&
(searchTerm === '' ||
translate('::' + p.displayName) translate('::' + p.displayName)
.toLowerCase() .toLowerCase()
.includes(lowerTerm), .includes(lowerTerm)),
) )
// 2⃣ Her bulunan parentın altındaki child izinleri ekle // Recursive children builder (her seviye için doğru level)
const result: PermissionWithStyle[] = [] function buildTree(
topLevelMatches.forEach((parent) => { parent: PermissionWithStyle,
result.push(parent) level: number,
): (PermissionWithStyle & { level: number })[] {
// Alt kırılımları bul const arr = [{ ...parent, level }]
const children = selectedGroupPermissions.filter((child) => child.parentName === parent.name) const parentKey = parent.name || ''
result.push(...children) if (openPermissions[parentKey] || searchTerm) {
const children = getChildren(parentKey)
children.forEach((child) => {
arr.push(...buildTree(child, level + 1))
}) })
}
return arr
}
// 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 return result
}, [selectedGroupPermissions, searchTerm, translate]) }, [selectedGroupPermissions, searchTerm, translate, openPermissions])
const onSelectAll = (value: boolean, e: ChangeEvent<HTMLInputElement>) => { const onSelectAll = (value: boolean, e: ChangeEvent<HTMLInputElement>) => {
if (!permissionList) { if (!permissionList) {
@ -284,6 +335,14 @@ function RolesPermission({
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}> <Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
{translate('AbpPermissionManagement::SelectAllInThisTab')} {translate('AbpPermissionManagement::SelectAllInThisTab')}
</Checkbox> </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> </div>
@ -310,26 +369,56 @@ function RolesPermission({
</div> </div>
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto"> <div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
<hr className="mb-2"></hr> <hr className="mb-2"></hr>
<div className="flex items-center gap-2 mb-2">
<Input <Input
size="sm" size="sm"
className="mb-2" className=""
placeholder={translate('::Search')} placeholder={translate('::Search')}
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
</div>
<div className="my-1"> <div className="my-1">
{filteredPermissions.map((permission) => ( {filteredPermissions.map((permission) => {
<div key={permission.name}> 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 <Checkbox
name={permission.name} name={permission.name}
className={permission.class}
checked={permission.isGranted} checked={permission.isGranted}
onChange={() => onClickCheckbox(permission)} onChange={() => onClickCheckbox(permission)}
> >
<span className="text-sm text-gray-700 group-hover:text-gray-900 transition">
{translate('::' + permission.displayName)} {translate('::' + permission.displayName)}
</span>
</Checkbox> </Checkbox>
</div> </div>
))} </div>
)
})}
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,6 +1,6 @@
import AdaptableCard from '@/components/shared/AdaptableCard' import { FaChevronRight, FaChevronDown } from 'react-icons/fa'
import Container from '@/components/shared/Container' import { Badge, Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
import { Badge, Button, Checkbox, Dialog, Menu, toast } from '@/components/ui' type OpenState = Record<string, boolean>
import { useConfig } from '@/components/ui/ConfigProvider' import { useConfig } from '@/components/ui/ConfigProvider'
import Notification from '@/components/ui/Notification' import Notification from '@/components/ui/Notification'
import { import {
@ -10,7 +10,6 @@ import {
PermissionWithGroupName, PermissionWithGroupName,
PermissionWithStyle, PermissionWithStyle,
ProviderInfoDto, ProviderInfoDto,
UpdatePermissionDto,
} from '@/proxy/admin/models' } from '@/proxy/admin/models'
import { getPermissions, updatePermissions } from '@/services/identity.service' import { getPermissions, updatePermissions } from '@/services/identity.service'
import { useStoreActions, useStoreState } from '@/store' 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 mode = useStoreState((state) => state.theme.mode)
const { direction } = useConfig() const { direction } = useConfig()
@ -216,6 +253,42 @@ function UsersPermission({
return false 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 ? ( return permissionList ? (
<Dialog <Dialog
width="min(900px, 95vw)" width="min(900px, 95vw)"
@ -224,7 +297,7 @@ function UsersPermission({
onClose={onDialogClose} onClose={onDialogClose}
onRequestClose={onDialogClose} onRequestClose={onDialogClose}
> >
<h5 className="mb-4"> <h5 className="mb-1">
{translate('::Permission')} - {name} {translate('::Permission')} - {name}
</h5> </h5>
<hr className="mt-1 mb-2"></hr> <hr className="mt-1 mb-2"></hr>
@ -239,12 +312,18 @@ function UsersPermission({
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}> <Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
{translate('AbpPermissionManagement::SelectAllInThisTab')} {translate('AbpPermissionManagement::SelectAllInThisTab')}
</Checkbox> </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> </div>
<div className="flex flex-col md:flex-row gap-4"> <div className="flex flex-col md:flex-row gap-4">
<div className="w-full md:w-1/3 max-h-[450px] overflow-y-auto"> <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 ?? '']}> <Menu variant={mode} defaultActiveKeys={[selectedGroup?.displayName ?? '']}>
{permissionList?.groups.map((group) => ( {permissionList?.groups.map((group) => (
<Menu.MenuItem <Menu.MenuItem
@ -262,31 +341,70 @@ function UsersPermission({
</Menu> </Menu>
</div> </div>
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto"> <div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
<hr className="mt-2 mb-2"></hr> <hr className="mb-2"></hr>
<div className="card-body"> <div className="flex items-center gap-2 mb-2">
{selectedGroupPermissions.map((permission) => ( <Input
<div key={permission.name}> 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 <Checkbox
name={permission.name} name={permission.name}
className={permission.class}
disabled={isGrantedByOtherProviderName(permission.grantedProviders)}
//disabled={permission.grantedProviders.length > 0 ? true : false}
checked={permission.isGranted} checked={permission.isGranted}
onChange={() => onClickCheckbox(permission)} 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)} {translate('::' + permission.displayName)}
{permission.grantedProviders.map((provider) => ( {permission.grantedProviders.map((provider) => {
const badgeContent = provider.providerName !== providerName
? `${provider.providerName}: ${provider.providerKey}`
: provider.providerName;
return (
<Badge <Badge
key={provider.providerKey} key={provider.providerKey}
className="m-2" className="m-2"
content={`${provider.providerName}${ content={badgeContent}
provider.providerName !== providerName ? `: ${provider.providerKey}` : ''
}`}
/> />
))} );
})}
</span>
</Checkbox> </Checkbox>
</div> </div>
))} </div>
)
})}
</div> </div>
</div> </div>
</div> </div>