sozsoft-platform/ui/src/views/admin/role-management/RolesPermission.tsx
2026-03-23 10:25:00 +03:00

351 lines
11 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Container from '@/components/shared/Container'
import { Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
import { useConfig } from '@/components/ui/ConfigProvider'
import Notification from '@/components/ui/Notification'
import {
GetPermissionListResultDto,
PermissionGrantInfoDto,
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'
function RolesPermission({
open,
onDialogClose,
name,
}: {
open: boolean
onDialogClose: () => void
name: string
}) {
const providerName = 'R'
const { translate } = useLocalization()
const { getConfig } = useStoreActions((a) => a.abpConfig)
const [isLoading, setIsLoading] = useState(false)
const [permissionList, setPermissionList] = useState<GetPermissionListResultDto>()
const [selectedGroup, setSelectedGroup] = useState<PermissionGroupDto | undefined>()
const [selectedGroupPermissions, setSelectedGroupPermissions] = useState<PermissionWithStyle[]>(
[],
)
const [searchTerm, setSearchTerm] = useState('')
const mode = useStoreState((state) => state.theme.mode)
const tenant = useStoreState((state) => state.auth.tenant)
const { direction } = useConfig()
const className = `m${direction[0]}-`
const fetchDataPermissions = async () => {
if (!name) return
const response = await getPermissions(providerName, name)
const data = response.data
const tenantGroup = tenant?.menuGroup?.trim()
if (!tenantGroup) {
console.warn('Tenant menuGroup boş, tüm izinler gösterilecek')
setPermissionList(data)
return
}
const filteredGroups = data.groups
.map((group: any) => {
const filteredPermissions = group.permissions.filter((perm: any) => {
if (!perm.menuGroup) return false
const groups =
typeof perm.menuGroup === 'string'
? perm.menuGroup.split('|').map((g: string) => g.trim())
: perm.menuGroup
return groups.includes(tenantGroup)
})
return { ...group, permissions: filteredPermissions }
})
.filter((group: any) => group.permissions.length > 0)
const filteredData = { ...data, groups: filteredGroups }
console.log('Filtered permissions by tenant group:', tenantGroup, filteredData)
setPermissionList(filteredData)
}
const changeGroup = (groupName?: string) => {
const group = permissionList?.groups.find((a: any) => a.name === groupName)
if (!group) {
setSelectedGroup(undefined)
setSelectedGroupPermissions([])
return
}
setSelectedGroup(group)
const selectedGroupPerm = group.permissions.map(
(permission: any) =>
({
...permission,
class: className + findMargin(group.permissions, permission) * 4,
}) as PermissionWithStyle,
)
setSelectedGroupPermissions(selectedGroupPerm)
}
const onDialogOk = async () => {
try {
setIsLoading(true)
if (permissionList?.groups && name) {
const listPermissions = await getPermissions(providerName, name)
const unChangedPermissions = getPermissionsWithGroupName(listPermissions.data?.groups)
const changedPermissions = getPermissionsWithGroupName(permissionList.groups)
const updatePermList = changedPermissions
.filter(
(per) =>
(unChangedPermissions.find((unChanged) => unChanged.name === per.name) || {})
.isGranted !== per.isGranted,
)
.map(({ name, isGranted }) => ({ name, isGranted }))
// 🔴 KRİTİK NOKTA
await updatePermissions(providerName, name, {
permissions: updatePermList,
})
// ✅ artık backend kesin güncel
await getConfig(false)
toast.push(<Notification title={'Permission updated'} type="success" />, {
placement: 'top-end',
})
}
onDialogClose()
} catch (err) {
console.error(err)
} finally {
setIsLoading(false)
}
}
function getPermissionsWithGroupName(groups: PermissionGroupDto[]): PermissionWithGroupName[] {
return groups.reduce(
(acc, val) => [
...acc,
...val.permissions.map<PermissionWithGroupName>((p: any) => ({
...p,
groupName: val.name || '',
})),
],
[] as PermissionWithGroupName[],
)
}
function findMargin(
permissions: PermissionGrantInfoDto[],
permission: PermissionGrantInfoDto,
level = 0,
): number {
const parentPermission = permissions.find((per) => per.name === permission.parentName)
if (parentPermission) {
return findMargin(permissions, parentPermission, level + 1)
} else {
return level
}
}
const isAllSelected = useMemo(
() =>
permissionList?.groups.every((group: any) =>
group.permissions.every((permission: any) => permission.isGranted),
),
[permissionList],
)
const isAllSelectedForGroup = useMemo(
() => selectedGroupPermissions.every((permission) => permission.isGranted),
[selectedGroupPermissions],
)
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(
(p) =>
!p.parentName && // parentName yoksa bu 1. kırınım demektir
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)
// Alt kırılımları bul
const children = selectedGroupPermissions.filter((child) => child.parentName === parent.name)
result.push(...children)
})
return result
}, [selectedGroupPermissions, searchTerm, translate])
const onSelectAll = (value: boolean, e: ChangeEvent<HTMLInputElement>) => {
if (!permissionList) {
return
}
const currentGroupName = selectedGroup?.name
setSelectedGroup(undefined)
setSelectedGroupPermissions([])
if (e.target.name === 'group') {
permissionList?.groups
.find((group: any) => group.name === selectedGroup?.name)
?.permissions.forEach((permission: any) => {
permission.isGranted = value
})
} else {
permissionList?.groups.forEach((group: any) => {
group.permissions.forEach((permission: any) => {
permission.isGranted = value
})
})
}
setPermissionList({ ...permissionList })
changeGroup(currentGroupName)
}
function onClickCheckbox(clickedPermission: PermissionGrantInfoDto) {
if (!permissionList) {
return
}
const groupPerm = selectedGroupPermissions.map((per) => {
if (clickedPermission.name === per.name) {
return { ...per, isGranted: !per.isGranted }
} else if (clickedPermission.name === per.parentName && clickedPermission.isGranted) {
return { ...per, isGranted: false }
} else if (clickedPermission.parentName === per.name && !clickedPermission.isGranted) {
return { ...per, isGranted: true }
}
return per
})
const permGroup = permissionList?.groups.find((a: any) => a.name === selectedGroup?.name)
if (permGroup) {
permGroup.permissions = groupPerm
}
setPermissionList({ ...permissionList })
setSelectedGroupPermissions(groupPerm)
}
useEffect(() => {
fetchDataPermissions()
}, [name])
return permissionList ? (
<Container>
<Dialog
width="min(900px, 95vw)"
isOpen={open}
onAfterOpen={() => changeGroup(permissionList?.groups[0].name)}
onClose={onDialogClose}
onRequestClose={onDialogClose}
>
<h5 className="mb-1">
{translate('::Permission')} - {name}
</h5>
<hr className="mt-1 mb-2"></hr>
<div className="flex flex-col md:flex-row gap-4 mb-1">
<div className="w-full md:w-1/3">
<Checkbox name="all" checked={isAllSelected} onChange={onSelectAll}>
{translate('AbpPermissionManagement::SelectAllInAllTabs')}
</Checkbox>
</div>
<div className="w-full md:w-2/3">
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
{translate('AbpPermissionManagement::SelectAllInThisTab')}
</Checkbox>
</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="mb-2"></hr>
<Menu
className="w-full"
variant={mode}
defaultActiveKeys={[selectedGroup?.displayName ?? '']}
>
{permissionList?.groups.map((group: any) => (
<Menu.MenuItem
key={group.name}
className="break-all whitespace-normal"
eventKey={group.name}
onSelect={changeGroup}
>
{translate('::' + group.displayName)} (
{group.permissions.filter((a: any) => a.isGranted).length})
</Menu.MenuItem>
))}
</Menu>
</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="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>
))}
</div>
</div>
</div>
<div className="text-right mt-6">
<Button className="ltr:mr-2 rtl:ml-2" variant="plain" onClick={onDialogClose}>
{translate('::Cancel')}
</Button>
<Button variant="solid" loading={isLoading} onClick={onDialogOk}>
{isLoading ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</div>
</Dialog>
</Container>
) : (
<></>
)
}
export default RolesPermission