2025-05-06 06:45:49 +00:00
|
|
|
|
import { ActionLink, ColumnDef, DataTable, Loading } from '@/components/shared'
|
|
|
|
|
|
import AdaptableCard from '@/components/shared/AdaptableCard'
|
|
|
|
|
|
import Container from '@/components/shared/Container'
|
|
|
|
|
|
import {
|
|
|
|
|
|
Button,
|
|
|
|
|
|
Card,
|
|
|
|
|
|
Dialog,
|
|
|
|
|
|
FormContainer,
|
|
|
|
|
|
FormItem,
|
|
|
|
|
|
Input,
|
|
|
|
|
|
Notification,
|
|
|
|
|
|
Select,
|
|
|
|
|
|
Table,
|
|
|
|
|
|
Tabs,
|
|
|
|
|
|
toast,
|
|
|
|
|
|
} from '@/components/ui'
|
|
|
|
|
|
import TBody from '@/components/ui/Table/TBody'
|
|
|
|
|
|
import Td from '@/components/ui/Table/Td'
|
|
|
|
|
|
import Th from '@/components/ui/Table/Th'
|
|
|
|
|
|
import THead from '@/components/ui/Table/THead'
|
|
|
|
|
|
import Tr from '@/components/ui/Table/Tr'
|
|
|
|
|
|
import TabContent from '@/components/ui/Tabs/TabContent'
|
|
|
|
|
|
import TabList from '@/components/ui/Tabs/TabList'
|
|
|
|
|
|
import TabNav from '@/components/ui/Tabs/TabNav'
|
2025-08-12 08:39:06 +00:00
|
|
|
|
import { getRoles, getUsers } from '@/services/identity.service'
|
|
|
|
|
|
import { IdentityRoleDto, IdentityUserDto } from '@/proxy/admin/models'
|
2025-05-06 06:45:49 +00:00
|
|
|
|
import {
|
|
|
|
|
|
CreateUpdateOrganizationUnitDto,
|
|
|
|
|
|
OrganizationUnitDto,
|
|
|
|
|
|
} from '@/proxy/admin/organization-unit/models'
|
|
|
|
|
|
import {
|
|
|
|
|
|
ouDelete,
|
|
|
|
|
|
ouDeleteMembers,
|
|
|
|
|
|
ouDeleteRoles,
|
|
|
|
|
|
ouGetAll,
|
|
|
|
|
|
ouGetMembers,
|
|
|
|
|
|
ouGetRoles,
|
|
|
|
|
|
ouMoveAllUsers,
|
|
|
|
|
|
ouPost,
|
|
|
|
|
|
ouPut,
|
|
|
|
|
|
ouPutMembers,
|
|
|
|
|
|
ouPutMove,
|
|
|
|
|
|
ouPutRoles,
|
2025-08-12 08:39:06 +00:00
|
|
|
|
} from '@/services/organization-unit.service'
|
2025-05-06 06:45:49 +00:00
|
|
|
|
import { SelectBoxOption } from '@/shared/types'
|
|
|
|
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
|
|
|
|
|
import TableNoRecords from '@/views/shared/TableNoRecords'
|
|
|
|
|
|
import classNames from 'classnames'
|
|
|
|
|
|
import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik'
|
|
|
|
|
|
import isEmpty from 'lodash/isEmpty'
|
|
|
|
|
|
import { memo, Suspense, useEffect, useMemo, useState } from 'react'
|
|
|
|
|
|
import { NodeApi, NodeRendererProps } from 'react-arborist'
|
|
|
|
|
|
import { Tree } from 'react-arborist/dist/module/components/tree'
|
|
|
|
|
|
import { Helmet } from 'react-helmet'
|
|
|
|
|
|
import {
|
2025-08-16 21:17:18 +00:00
|
|
|
|
FaCheckCircle,
|
|
|
|
|
|
FaUser,
|
|
|
|
|
|
FaFolder,
|
|
|
|
|
|
FaMinusCircle,
|
|
|
|
|
|
FaPlusCircle,
|
|
|
|
|
|
FaTrash,
|
|
|
|
|
|
FaSitemap,
|
|
|
|
|
|
FaCogs,
|
|
|
|
|
|
FaTrashAlt,
|
|
|
|
|
|
FaEdit,
|
|
|
|
|
|
FaUsers,
|
|
|
|
|
|
FaUserPlus,
|
|
|
|
|
|
FaUserShield,
|
|
|
|
|
|
} from 'react-icons/fa'
|
2025-05-06 06:45:49 +00:00
|
|
|
|
import { object, string } from 'yup'
|
2025-08-21 14:57:00 +00:00
|
|
|
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
2025-05-06 06:45:49 +00:00
|
|
|
|
|
|
|
|
|
|
const schema = object().shape({
|
|
|
|
|
|
id: string(),
|
|
|
|
|
|
parentId: string(),
|
|
|
|
|
|
displayName: string().required(),
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const schemaMoveAllUsers = object().shape({
|
|
|
|
|
|
id: string().required(),
|
|
|
|
|
|
newId: string().required(),
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
interface TreeViewData {
|
|
|
|
|
|
id: string
|
|
|
|
|
|
name: string
|
|
|
|
|
|
children?: TreeViewData[]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const convertToTreeViewData = (
|
|
|
|
|
|
items: (OrganizationUnitDto & { isAdded?: boolean })[],
|
|
|
|
|
|
parentId?: string,
|
|
|
|
|
|
) => {
|
|
|
|
|
|
const levelItems = items.filter((a) => a.parentId == parentId)
|
|
|
|
|
|
if (!levelItems?.length) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const data: TreeViewData[] = []
|
|
|
|
|
|
|
|
|
|
|
|
for (let item of levelItems) {
|
|
|
|
|
|
if (!item.id || item.isAdded) {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
item.isAdded = true
|
|
|
|
|
|
data.push({
|
|
|
|
|
|
id: item.id,
|
|
|
|
|
|
name: item.displayName,
|
|
|
|
|
|
children: convertToTreeViewData(items, item.id),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return data
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface DeleteTypeData {
|
|
|
|
|
|
id: string
|
|
|
|
|
|
name: 'Organization Unit' | 'User' | 'Role'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const OrganizationUnits = () => {
|
|
|
|
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
|
|
const [ous, setOus] = useState<SelectBoxOption[]>([])
|
|
|
|
|
|
const [list, setList] = useState<TreeViewData[]>([])
|
|
|
|
|
|
const [activeOu, setActiveOu] = useState<string>()
|
|
|
|
|
|
const [dialogItem, setDialogItem] = useState<CreateUpdateOrganizationUnitDto | undefined>()
|
|
|
|
|
|
const [ouMembers, setOuMembers] = useState<IdentityUserDto[]>([])
|
|
|
|
|
|
const [userSelectionList, setUserSelectionList] = useState<IdentityUserDto[]>([])
|
|
|
|
|
|
const [userSelectedRows, setUserSelectedRows] = useState<string[]>([])
|
|
|
|
|
|
const [roleSelectionList, setRoleSelectionList] = useState<IdentityRoleDto[]>([])
|
|
|
|
|
|
const [ouRoles, setOuRoles] = useState<IdentityRoleDto[]>([])
|
|
|
|
|
|
const [roleSelectedRows, setRoleSelectedRows] = useState<string[]>([])
|
|
|
|
|
|
const [deleteRow, setDeleteRow] = useState<DeleteTypeData | null>()
|
|
|
|
|
|
const [isMoveAllUsersOpen, setIsMoveAllUsersOpen] = useState(false)
|
|
|
|
|
|
const [treeSearch, setTreeSearch] = useState('')
|
|
|
|
|
|
|
|
|
|
|
|
const { translate } = useLocalization()
|
|
|
|
|
|
|
|
|
|
|
|
const userSelectionColumns: ColumnDef<IdentityUserDto>[] = useMemo(
|
|
|
|
|
|
() => [
|
|
|
|
|
|
{
|
|
|
|
|
|
header: 'Name',
|
|
|
|
|
|
accessorKey: 'fullName',
|
|
|
|
|
|
cell: ({ row }) => (
|
|
|
|
|
|
<>
|
|
|
|
|
|
{row.original.name} {row.original.surname}
|
|
|
|
|
|
</>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
header: 'Email',
|
|
|
|
|
|
accessorKey: 'email',
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
[],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const roleSelectionColumns: ColumnDef<IdentityRoleDto>[] = useMemo(
|
|
|
|
|
|
() => [
|
|
|
|
|
|
{
|
|
|
|
|
|
header: 'Name',
|
|
|
|
|
|
accessorKey: 'Name',
|
|
|
|
|
|
cell: ({ row }) => <>{row.original.name}</>,
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
[],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const handleUserRowSelect = (checked: boolean, row: IdentityUserDto) => {
|
|
|
|
|
|
if (checked) {
|
|
|
|
|
|
const newState = [...userSelectedRows, row.id!]
|
|
|
|
|
|
setUserSelectedRows(newState)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const newState = [...userSelectedRows.filter((id) => id !== row.id)]
|
|
|
|
|
|
setUserSelectedRows(newState)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleRoleRowSelect = (checked: boolean, row: IdentityRoleDto) => {
|
|
|
|
|
|
if (checked) {
|
|
|
|
|
|
const newState = [...roleSelectedRows, row.id!]
|
|
|
|
|
|
setRoleSelectedRows(newState)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const newState = [...roleSelectedRows.filter((id) => id !== row.id)]
|
|
|
|
|
|
setRoleSelectedRows(newState)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const fetchData = async () => {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
const response = await ouGetAll()
|
|
|
|
|
|
if (response.data?.items) {
|
|
|
|
|
|
setOus(
|
|
|
|
|
|
response.data.items.map((a) => ({
|
|
|
|
|
|
value: a.id,
|
|
|
|
|
|
label: a.displayName,
|
|
|
|
|
|
})),
|
|
|
|
|
|
)
|
|
|
|
|
|
var list = convertToTreeViewData(response.data.items) ?? []
|
|
|
|
|
|
setList(list)
|
|
|
|
|
|
}
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (isEmpty(list)) {
|
|
|
|
|
|
fetchData()
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
|
|
const handleDialogSubmit = async (
|
|
|
|
|
|
values: CreateUpdateOrganizationUnitDto,
|
|
|
|
|
|
{ setSubmitting }: FormikHelpers<CreateUpdateOrganizationUnitDto>,
|
|
|
|
|
|
) => {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
setSubmitting(true)
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (values.id) {
|
|
|
|
|
|
await ouPut(values)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await ouPost(values)
|
|
|
|
|
|
}
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="success" duration={2000}>
|
|
|
|
|
|
{translate('::Kaydet')}
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
fetchData()
|
|
|
|
|
|
onDialogClose()
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="danger" duration={2000}>
|
|
|
|
|
|
Hata
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
setSubmitting(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onDialogClose = () => {
|
|
|
|
|
|
setDialogItem(undefined)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onDropdownItemClick = (node: NodeApi<TreeViewData>, eventKey: string) => {
|
|
|
|
|
|
if (eventKey == 'edit') {
|
|
|
|
|
|
setDialogItem({
|
|
|
|
|
|
id: node.data.id,
|
|
|
|
|
|
displayName: node.data.name,
|
|
|
|
|
|
parentId: node.parent?.isRoot ? undefined : node.parent?.data.id,
|
|
|
|
|
|
})
|
|
|
|
|
|
} else if (eventKey == 'add-sub-unit') {
|
|
|
|
|
|
setDialogItem({
|
|
|
|
|
|
displayName: '',
|
|
|
|
|
|
parentId: node.data.id,
|
|
|
|
|
|
})
|
|
|
|
|
|
} else if (eventKey == 'delete') {
|
|
|
|
|
|
setDeleteRow({ id: node.data.id, name: 'Organization Unit' })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onSelect = async (nodes: NodeApi<TreeViewData>[]) => {
|
|
|
|
|
|
if (nodes.length === 0) {
|
|
|
|
|
|
setActiveOu(undefined)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const node = nodes[0]
|
|
|
|
|
|
setActiveOu(node.data.id)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const fetchUsersAndRoles = async (id: string) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
const memberList = await ouGetMembers(id)
|
|
|
|
|
|
const members = memberList.data?.items ?? []
|
|
|
|
|
|
setOuMembers(members)
|
|
|
|
|
|
setUserSelectedRows(members.map((a) => a.id!))
|
|
|
|
|
|
|
|
|
|
|
|
const roleList = await ouGetRoles(id)
|
|
|
|
|
|
const roles = roleList.data?.items ?? []
|
|
|
|
|
|
setOuRoles(roles)
|
|
|
|
|
|
setRoleSelectedRows(roles.map((a) => a.id!))
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="danger" duration={2000}>
|
|
|
|
|
|
Hata
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
const fn = async () => {
|
|
|
|
|
|
setOuMembers([])
|
|
|
|
|
|
setUserSelectedRows([])
|
|
|
|
|
|
setOuRoles([])
|
|
|
|
|
|
setRoleSelectedRows([])
|
|
|
|
|
|
|
|
|
|
|
|
if (activeOu) {
|
|
|
|
|
|
await fetchUsersAndRoles(activeOu)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
fn()
|
|
|
|
|
|
}, [activeOu])
|
|
|
|
|
|
|
|
|
|
|
|
const handleEdit = async (id: string, name: string) => {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (!id) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await ouPut({ id, displayName: name })
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="success" duration={2000}>
|
|
|
|
|
|
{translate('::Kaydet')}
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
fetchData()
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="danger" duration={2000}>
|
|
|
|
|
|
Hata
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleMove = async (id: string, newParentId?: string) => {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (!id) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await ouPutMove(id, newParentId)
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="success" duration={2000}>
|
|
|
|
|
|
{translate('::Kaydet')}
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
fetchData()
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="danger" duration={2000}>
|
|
|
|
|
|
Hata
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Node = memo(({ node, style, dragHandle }: NodeRendererProps<any>) => {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div
|
|
|
|
|
|
className={classNames(
|
|
|
|
|
|
'h-6 text-md flex gap-2 items-center',
|
|
|
|
|
|
node.isSelected ? 'bg-indigo-200' : '',
|
|
|
|
|
|
)}
|
|
|
|
|
|
style={style}
|
|
|
|
|
|
ref={dragHandle}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div
|
|
|
|
|
|
className="flex items-center h-full w-full"
|
|
|
|
|
|
onClick={() => node.isInternal && node.toggle()}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="w-3">
|
|
|
|
|
|
{node.isInternal && (node.isOpen ? <FaMinusCircle /> : <FaPlusCircle />)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<FaFolder className="ml-2 w-3" color="#337ab7" />
|
|
|
|
|
|
|
|
|
|
|
|
<span className="node-text m-1">
|
|
|
|
|
|
{node.isEditing ? (
|
|
|
|
|
|
<Input
|
|
|
|
|
|
className="h-4"
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
defaultValue={node.data.name}
|
|
|
|
|
|
onBlur={() => node.reset()}
|
|
|
|
|
|
onKeyDown={(e) => {
|
|
|
|
|
|
if (e.key === 'Escape') {
|
|
|
|
|
|
node.reset()
|
|
|
|
|
|
}
|
|
|
|
|
|
if (e.key === 'Enter') {
|
|
|
|
|
|
node.submit(e.currentTarget.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
autoFocus
|
|
|
|
|
|
/>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<span>{node.data.name}</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="file-actions">
|
|
|
|
|
|
<div className="flex gap-1 folderFileActions">
|
2025-08-12 08:39:06 +00:00
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => setIsMoveAllUsersOpen(true)}
|
|
|
|
|
|
title={translate('::Abp.Identity.OrganizationUnit.MoveAllUsers')}
|
|
|
|
|
|
>
|
2025-08-16 21:17:18 +00:00
|
|
|
|
<FaUserPlus size="20" color="#2d6da3" />
|
2025-05-06 06:45:49 +00:00
|
|
|
|
</button>
|
2025-08-12 08:39:06 +00:00
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => node.edit()}
|
|
|
|
|
|
title={translate('::Abp.Identity.OrganizationUnit.Rename')}
|
|
|
|
|
|
>
|
2025-08-16 21:17:18 +00:00
|
|
|
|
<FaEdit size="20" className="text-teal-900" />
|
2025-05-06 06:45:49 +00:00
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => setDeleteRow({ id: node.data.id, name: 'Organization Unit' })}
|
2025-08-12 08:39:06 +00:00
|
|
|
|
title={translate('::Delete')}
|
2025-05-06 06:45:49 +00:00
|
|
|
|
>
|
2025-08-16 21:17:18 +00:00
|
|
|
|
<FaTrashAlt size="20" className="text-red-500" />
|
2025-05-06 06:45:49 +00:00
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Helmet
|
2025-09-13 11:46:34 +00:00
|
|
|
|
titleTemplate="%s | Sözsoft Kurs Platform"
|
2025-05-06 06:45:49 +00:00
|
|
|
|
title={translate('::Abp.Identity.OrganizationUnits')}
|
2025-09-13 11:46:34 +00:00
|
|
|
|
defaultTitle="Sözsoft Kurs Platform"
|
2025-05-06 06:45:49 +00:00
|
|
|
|
></Helmet>
|
2025-09-29 14:54:43 +00:00
|
|
|
|
<Loading type="cover" className="h-full" loading={loading}>
|
|
|
|
|
|
<Container className="h-full">
|
|
|
|
|
|
<div className="flex flex-col lg:flex-row gap-4 h-full">
|
2025-08-13 14:58:33 +00:00
|
|
|
|
<Card
|
2025-09-29 14:54:43 +00:00
|
|
|
|
className="md:w-3/12 min-w-fit h-full"
|
2025-08-13 14:58:33 +00:00
|
|
|
|
header={translate('::Abp.Identity.OrganizationUnits')}
|
|
|
|
|
|
headerExtra={
|
|
|
|
|
|
<div className="flex gap-1">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
size="xs"
|
|
|
|
|
|
title={translate('::Abp.Identity.OrganizationUnit.AddUnit')}
|
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
|
setDialogItem({
|
|
|
|
|
|
parentId: activeOu,
|
|
|
|
|
|
displayName: '',
|
|
|
|
|
|
})
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2025-08-16 21:17:18 +00:00
|
|
|
|
{activeOu ? <FaSitemap /> : <FaCogs />}
|
2025-08-13 14:58:33 +00:00
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
size="xs"
|
|
|
|
|
|
title={translate('::Abp.Identity.OrganizationUnit.MoveAllUsers')}
|
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
|
setIsMoveAllUsersOpen(true)
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2025-08-16 21:17:18 +00:00
|
|
|
|
<FaUsers />
|
2025-08-13 14:58:33 +00:00
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
className="h-8"
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
placeholder={translate('::App.Search')}
|
|
|
|
|
|
value={treeSearch}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
setTreeSearch(e.target.value)
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Tree
|
|
|
|
|
|
openByDefault={false}
|
|
|
|
|
|
data={list}
|
|
|
|
|
|
onSelect={onSelect}
|
|
|
|
|
|
onMove={({ dragIds, parentId }) => {
|
|
|
|
|
|
if (dragIds.length > 0) {
|
|
|
|
|
|
handleMove(dragIds[0], parentId ?? '')
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
onRename={({ id, name }) => {
|
|
|
|
|
|
handleEdit(id, name)
|
|
|
|
|
|
}}
|
|
|
|
|
|
className="mt-2 cursor-pointer"
|
|
|
|
|
|
searchTerm={treeSearch}
|
|
|
|
|
|
indent={10}
|
|
|
|
|
|
searchMatch={(node, term) =>
|
|
|
|
|
|
node.data.name.toLowerCase().includes(term.toLowerCase())
|
2025-05-06 06:45:49 +00:00
|
|
|
|
}
|
2025-08-13 14:58:33 +00:00
|
|
|
|
width={'100%'}
|
2025-05-06 06:45:49 +00:00
|
|
|
|
>
|
2025-08-13 14:58:33 +00:00
|
|
|
|
{Node}
|
|
|
|
|
|
</Tree>
|
|
|
|
|
|
</Card>
|
2025-08-13 20:29:27 +00:00
|
|
|
|
<Card className="md:w-9/12 w-full">
|
2025-08-13 14:58:33 +00:00
|
|
|
|
<Tabs defaultValue="users">
|
|
|
|
|
|
<TabList>
|
2025-08-16 21:17:18 +00:00
|
|
|
|
<TabNav value="users" icon={<FaUser />}>
|
2025-08-13 14:58:33 +00:00
|
|
|
|
{translate('::AbpIdentity.Users')}
|
|
|
|
|
|
</TabNav>
|
2025-08-16 21:17:18 +00:00
|
|
|
|
<TabNav value="roles" icon={<FaCheckCircle />}>
|
2025-08-13 14:58:33 +00:00
|
|
|
|
{translate('::AbpIdentity.Roles')}
|
|
|
|
|
|
</TabNav>
|
|
|
|
|
|
</TabList>
|
|
|
|
|
|
<TabContent value="users">
|
|
|
|
|
|
{activeOu ? (
|
|
|
|
|
|
<AdaptableCard
|
|
|
|
|
|
headerClass="text-right"
|
|
|
|
|
|
header={
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
size="sm"
|
|
|
|
|
|
onClick={async (e) => {
|
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
|
const response = await getUsers(0, 1000)
|
|
|
|
|
|
setUserSelectionList(response.data?.items ?? [])
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2025-08-16 21:17:18 +00:00
|
|
|
|
<FaUserPlus />
|
2025-08-13 14:58:33 +00:00
|
|
|
|
</Button>
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Table compact>
|
|
|
|
|
|
{!!ouMembers.length && (
|
|
|
|
|
|
<THead>
|
|
|
|
|
|
<Tr>
|
|
|
|
|
|
<Th></Th>
|
|
|
|
|
|
<Th>Adı Soyadı</Th>
|
|
|
|
|
|
<Th>E-Posta</Th>
|
|
|
|
|
|
<Th>Durum</Th>
|
|
|
|
|
|
</Tr>
|
|
|
|
|
|
</THead>
|
|
|
|
|
|
)}
|
|
|
|
|
|
<TBody>
|
|
|
|
|
|
{ouMembers.map((user) => (
|
|
|
|
|
|
<Tr key={user.id}>
|
|
|
|
|
|
<Td>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
className="mr-auto"
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
size="xs"
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setDeleteRow({
|
|
|
|
|
|
id: user.id ?? '',
|
|
|
|
|
|
name: 'User',
|
|
|
|
|
|
})
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaTrash />
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Td>
|
|
|
|
|
|
<Td>
|
|
|
|
|
|
<ActionLink
|
2025-08-21 14:57:00 +00:00
|
|
|
|
href={ROUTES_ENUM.protected.admin.identity.user.detail.replace(
|
|
|
|
|
|
':userId',
|
|
|
|
|
|
user.id!,
|
|
|
|
|
|
)}
|
2025-08-13 14:58:33 +00:00
|
|
|
|
className="font-bold"
|
|
|
|
|
|
target="_blank"
|
|
|
|
|
|
>
|
|
|
|
|
|
{user.name} {user.surname}
|
|
|
|
|
|
</ActionLink>
|
|
|
|
|
|
</Td>
|
|
|
|
|
|
<Td>{user.email}</Td>
|
|
|
|
|
|
<Td>{user.isActive ? 'Aktif' : 'Pasif'}</Td>
|
|
|
|
|
|
</Tr>
|
|
|
|
|
|
))}
|
|
|
|
|
|
<TableNoRecords show={!ouMembers.length} />
|
|
|
|
|
|
</TBody>
|
|
|
|
|
|
</Table>
|
|
|
|
|
|
</AdaptableCard>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className="p-3">
|
|
|
|
|
|
{translate('::Abp.Identity.OrganizationUnit.Users.Description')}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</TabContent>
|
|
|
|
|
|
<TabContent value="roles">
|
|
|
|
|
|
{activeOu ? (
|
|
|
|
|
|
<AdaptableCard
|
|
|
|
|
|
headerClass="text-right"
|
|
|
|
|
|
header={
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
size="sm"
|
|
|
|
|
|
onClick={async (e) => {
|
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
|
const response = await getRoles(0, 1000)
|
|
|
|
|
|
setRoleSelectionList(response.data?.items ?? [])
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2025-08-16 21:17:18 +00:00
|
|
|
|
<FaUserPlus />
|
2025-08-13 14:58:33 +00:00
|
|
|
|
</Button>
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Table compact>
|
|
|
|
|
|
{!!ouRoles.length && (
|
|
|
|
|
|
<THead>
|
|
|
|
|
|
<Tr>
|
|
|
|
|
|
<Th></Th>
|
|
|
|
|
|
<Th>Rol</Th>
|
|
|
|
|
|
</Tr>
|
|
|
|
|
|
</THead>
|
|
|
|
|
|
)}
|
|
|
|
|
|
<TBody>
|
|
|
|
|
|
{ouRoles.map((role) => (
|
|
|
|
|
|
<Tr key={role.id}>
|
|
|
|
|
|
<Td>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
className="mr-auto"
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
size="xs"
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setDeleteRow({
|
|
|
|
|
|
id: role.id ?? '',
|
|
|
|
|
|
name: 'Role',
|
|
|
|
|
|
})
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaTrash />
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Td>
|
|
|
|
|
|
<Td>{role.name}</Td>
|
|
|
|
|
|
</Tr>
|
|
|
|
|
|
))}
|
|
|
|
|
|
<TableNoRecords show={!ouRoles.length} />
|
|
|
|
|
|
</TBody>
|
|
|
|
|
|
</Table>
|
|
|
|
|
|
</AdaptableCard>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className="p-3">
|
|
|
|
|
|
{translate('::Abp.Identity.OrganizationUnit.Roles.Description')}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</TabContent>
|
|
|
|
|
|
</Tabs>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</div>
|
2025-05-06 06:45:49 +00:00
|
|
|
|
</Container>
|
|
|
|
|
|
</Loading>
|
|
|
|
|
|
|
|
|
|
|
|
<Dialog isOpen={!!dialogItem} onClose={onDialogClose} onRequestClose={onDialogClose}>
|
|
|
|
|
|
<h5 className="mb-4">{translate('::Abp.Identity.OrganizationUnit.NewUnit')}</h5>
|
|
|
|
|
|
<hr className="my-2"></hr>
|
|
|
|
|
|
<Suspense fallback={<></>}>
|
|
|
|
|
|
<Formik
|
|
|
|
|
|
initialValues={dialogItem ?? ({ displayName: '' } as CreateUpdateOrganizationUnitDto)}
|
|
|
|
|
|
validationSchema={schema}
|
|
|
|
|
|
onSubmit={handleDialogSubmit}
|
|
|
|
|
|
>
|
|
|
|
|
|
{({ touched, errors, isSubmitting }) => {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Form>
|
|
|
|
|
|
<FormContainer size="sm">
|
|
|
|
|
|
<FormItem
|
2025-05-28 13:47:54 +00:00
|
|
|
|
label={translate('::Abp.Identity.OrganizationUnit.Parent')}
|
2025-05-06 06:45:49 +00:00
|
|
|
|
invalid={errors.parentId && touched.parentId}
|
|
|
|
|
|
errorMessage={errors.parentId}
|
|
|
|
|
|
>
|
|
|
|
|
|
{/* Parent Name göster */}
|
|
|
|
|
|
<Field
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
disabled
|
|
|
|
|
|
autoComplete="off"
|
|
|
|
|
|
name="parentId"
|
|
|
|
|
|
component={Input}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</FormItem>
|
|
|
|
|
|
|
|
|
|
|
|
<FormItem
|
2025-05-28 13:47:54 +00:00
|
|
|
|
label={translate('::Abp.Identity.OrganizationUnit.DisplayName')}
|
2025-05-06 06:45:49 +00:00
|
|
|
|
invalid={errors.displayName && touched.displayName}
|
|
|
|
|
|
errorMessage={errors.displayName}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Field
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
autoComplete="off"
|
|
|
|
|
|
name="displayName"
|
|
|
|
|
|
component={Input}
|
|
|
|
|
|
autoFocus
|
|
|
|
|
|
/>
|
|
|
|
|
|
</FormItem>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="mt-6 flex flex-row justify-end gap-3">
|
|
|
|
|
|
<Button variant="solid" loading={isSubmitting} type="submit">
|
|
|
|
|
|
{isSubmitting ? translate('::...') : translate('::Save')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button type="button" variant="plain" onClick={onDialogClose}>
|
|
|
|
|
|
{translate('::Cancel')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</FormContainer>
|
|
|
|
|
|
</Form>
|
|
|
|
|
|
)
|
|
|
|
|
|
}}
|
|
|
|
|
|
</Formik>
|
|
|
|
|
|
</Suspense>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<Dialog
|
|
|
|
|
|
id="userSelection"
|
|
|
|
|
|
isOpen={userSelectionList.length > 0}
|
|
|
|
|
|
onClose={() => setUserSelectionList([])}
|
|
|
|
|
|
onRequestClose={() => setUserSelectionList([])}
|
|
|
|
|
|
>
|
2025-08-12 08:39:06 +00:00
|
|
|
|
<h5 className="mb-4">{translate('::Abp.Identity.OrganizationUnit.SelectMembers')}</h5>
|
2025-05-06 06:45:49 +00:00
|
|
|
|
|
|
|
|
|
|
<DataTable<IdentityUserDto>
|
|
|
|
|
|
selectable
|
|
|
|
|
|
selectedRows={[...userSelectedRows]}
|
|
|
|
|
|
columns={userSelectionColumns}
|
|
|
|
|
|
data={userSelectionList}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
// pagingData={tableData}
|
|
|
|
|
|
// onPaginationChange={handlePaginationChange}
|
|
|
|
|
|
// onSelectChange={handleSelectChange} // Items per page
|
|
|
|
|
|
// onSort={handleSort}
|
|
|
|
|
|
onCheckBoxChange={handleUserRowSelect}
|
|
|
|
|
|
// onIndeterminateCheckBoxChange={handleAllRowSelect}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="text-right mt-6">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
className="ltr:mr-2 rtl:ml-2"
|
|
|
|
|
|
variant="plain"
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setUserSelectionList([])
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{translate('::Cancel')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
|
if (!activeOu) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
await ouPutMembers(activeOu, userSelectedRows)
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="success" duration={2000}>
|
|
|
|
|
|
Updated
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
await fetchUsersAndRoles(activeOu)
|
|
|
|
|
|
setUserSelectionList([])
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="danger" duration={2000}>
|
|
|
|
|
|
Hata
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{translate('::Abp.Identity.OrganizationUnit.AddMembers')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<Dialog
|
|
|
|
|
|
id="roleSelection"
|
|
|
|
|
|
isOpen={roleSelectionList.length > 0}
|
|
|
|
|
|
onClose={() => setRoleSelectionList([])}
|
|
|
|
|
|
onRequestClose={() => setRoleSelectionList([])}
|
|
|
|
|
|
>
|
2025-08-12 08:39:06 +00:00
|
|
|
|
<h5 className="mb-4">{translate('::Abp.Identity.OrganizationUnit.SelectRoles')}</h5>
|
2025-05-06 06:45:49 +00:00
|
|
|
|
|
|
|
|
|
|
<DataTable<IdentityRoleDto>
|
|
|
|
|
|
selectable
|
|
|
|
|
|
selectedRows={[...roleSelectedRows]}
|
|
|
|
|
|
columns={roleSelectionColumns}
|
|
|
|
|
|
data={roleSelectionList}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
// pagingData={tableData}
|
|
|
|
|
|
// onPaginationChange={handlePaginationChange}
|
|
|
|
|
|
// onSelectChange={handleSelectChange} // Items per page
|
|
|
|
|
|
// onSort={handleSort}
|
|
|
|
|
|
onCheckBoxChange={handleRoleRowSelect}
|
|
|
|
|
|
// onIndeterminateCheckBoxChange={handleAllRowSelect}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="text-right mt-6">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
className="ltr:mr-2 rtl:ml-2"
|
|
|
|
|
|
variant="plain"
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setRoleSelectionList([])
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{translate('::Cancel')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
|
if (!activeOu) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
await ouPutRoles(activeOu, roleSelectedRows)
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="success" duration={2000}>
|
|
|
|
|
|
{translate('::ListForms.FormBilgileriKaydedildi')}
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
await fetchUsersAndRoles(activeOu)
|
|
|
|
|
|
setRoleSelectionList([])
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="danger" duration={2000}>
|
|
|
|
|
|
Hata
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{translate('::Abp.Identity.OrganizationUnit.AddRoles')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<Dialog
|
|
|
|
|
|
id="confirmDelete"
|
|
|
|
|
|
isOpen={!!deleteRow}
|
|
|
|
|
|
onClose={() => setDeleteRow(null)}
|
|
|
|
|
|
onRequestClose={() => setDeleteRow(null)}
|
|
|
|
|
|
>
|
2025-08-12 08:39:06 +00:00
|
|
|
|
<h5 className="mb-4">{translate('::Delete')}</h5>
|
2025-05-06 06:45:49 +00:00
|
|
|
|
|
2025-08-12 08:39:06 +00:00
|
|
|
|
<p>{translate('::DeleteConfirmation')}</p>
|
2025-05-06 06:45:49 +00:00
|
|
|
|
|
|
|
|
|
|
<div className="text-right mt-6">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
className="ltr:mr-2 rtl:ml-2"
|
|
|
|
|
|
variant="plain"
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setDeleteRow(null)
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2025-08-12 08:39:06 +00:00
|
|
|
|
{translate('::Cancel')}
|
2025-05-06 06:45:49 +00:00
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
|
if (!deleteRow) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
switch (deleteRow.name) {
|
|
|
|
|
|
case 'Organization Unit':
|
|
|
|
|
|
await ouDelete(deleteRow.id)
|
|
|
|
|
|
await fetchData()
|
|
|
|
|
|
break
|
|
|
|
|
|
case 'Role':
|
|
|
|
|
|
if (activeOu) {
|
|
|
|
|
|
await ouDeleteRoles(activeOu, deleteRow.id)
|
|
|
|
|
|
await fetchUsersAndRoles(activeOu)
|
|
|
|
|
|
}
|
|
|
|
|
|
break
|
|
|
|
|
|
case 'User':
|
|
|
|
|
|
if (activeOu) {
|
|
|
|
|
|
await ouDeleteMembers(activeOu, deleteRow.id)
|
|
|
|
|
|
await fetchUsersAndRoles(activeOu)
|
|
|
|
|
|
}
|
|
|
|
|
|
break
|
|
|
|
|
|
default:
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
setDeleteRow(null)
|
|
|
|
|
|
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="success" duration={2000}>
|
|
|
|
|
|
{translate('::KayitSilindi')}
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="danger" duration={2000}>
|
|
|
|
|
|
Hata
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2025-08-12 08:39:06 +00:00
|
|
|
|
{translate('::Delete')}
|
2025-05-06 06:45:49 +00:00
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<Dialog
|
|
|
|
|
|
id="moveAllUsers"
|
|
|
|
|
|
isOpen={isMoveAllUsersOpen}
|
|
|
|
|
|
onClose={() => setIsMoveAllUsersOpen(false)}
|
|
|
|
|
|
onRequestClose={() => setIsMoveAllUsersOpen(false)}
|
|
|
|
|
|
>
|
2025-05-28 13:47:54 +00:00
|
|
|
|
<h5 className="mb-4">{translate('::Abp.Identity.OrganizationUnit.MoveAllUsers')}</h5>
|
2025-05-06 06:45:49 +00:00
|
|
|
|
|
|
|
|
|
|
<Formik
|
|
|
|
|
|
initialValues={{ id: activeOu, newId: '' }}
|
|
|
|
|
|
validationSchema={schemaMoveAllUsers}
|
|
|
|
|
|
onSubmit={async (values, { setSubmitting }) => {
|
|
|
|
|
|
if (!values.id || values.id === values.newId) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setSubmitting(true)
|
|
|
|
|
|
try {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
await ouMoveAllUsers(values.id, values.newId)
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="success" duration={2000}>
|
|
|
|
|
|
{translate('::Abp.Identity.OrganizationUnit.MoveAllUsersMessage')}
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
await fetchUsersAndRoles(values.id)
|
|
|
|
|
|
setIsMoveAllUsersOpen(false)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast.push(
|
|
|
|
|
|
<Notification type="danger" duration={2000}>
|
|
|
|
|
|
Hata
|
|
|
|
|
|
</Notification>,
|
|
|
|
|
|
{
|
2025-09-01 14:07:03 +00:00
|
|
|
|
placement: 'top-end',
|
2025-05-06 06:45:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setSubmitting(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{({ touched, errors, values, isSubmitting }) => {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Form>
|
|
|
|
|
|
<FormContainer size="sm">
|
|
|
|
|
|
<FormItem
|
|
|
|
|
|
label={translate('::Abp.Identity.OrganizationUnit.CurrentOrganizationUnit')}
|
|
|
|
|
|
invalid={errors.id && touched.id}
|
|
|
|
|
|
errorMessage={errors.id}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Field
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
autoComplete="off"
|
|
|
|
|
|
name="id"
|
|
|
|
|
|
placeholder={translate(
|
|
|
|
|
|
'::Abp.Identity.OrganizationUnit.CurrentOrganizationUnit',
|
|
|
|
|
|
)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{({ field, form }: FieldProps<OrganizationUnitDto>) => (
|
|
|
|
|
|
<Select
|
|
|
|
|
|
field={field}
|
|
|
|
|
|
form={form}
|
|
|
|
|
|
isClearable={true}
|
|
|
|
|
|
options={ous}
|
|
|
|
|
|
value={ous?.filter((option) => option.value === values.id)}
|
|
|
|
|
|
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Field>
|
|
|
|
|
|
</FormItem>
|
|
|
|
|
|
<FormItem
|
|
|
|
|
|
label={translate('::Abp.Identity.OrganizationUnit.NewOrganizationUnit')}
|
|
|
|
|
|
invalid={errors.newId && touched.newId}
|
|
|
|
|
|
errorMessage={errors.newId}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Field
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
autoComplete="off"
|
|
|
|
|
|
name="newId"
|
|
|
|
|
|
placeholder={translate('::Abp.Identity.OrganizationUnit.NewOrganizationUnit')}
|
|
|
|
|
|
>
|
|
|
|
|
|
{({ field, form }: FieldProps<OrganizationUnitDto>) => (
|
|
|
|
|
|
<Select
|
|
|
|
|
|
field={field}
|
|
|
|
|
|
form={form}
|
|
|
|
|
|
isClearable={true}
|
|
|
|
|
|
options={ous}
|
|
|
|
|
|
value={ous?.filter((option) => option.value === values.newId)}
|
|
|
|
|
|
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Field>
|
|
|
|
|
|
</FormItem>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="mt-6 flex flex-row justify-end gap-3">
|
|
|
|
|
|
<Button variant="solid" loading={isSubmitting} type="submit">
|
|
|
|
|
|
{isSubmitting ? translate('::...') : translate('::Save')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
variant="plain"
|
|
|
|
|
|
onClick={() => setIsMoveAllUsersOpen(false)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{translate('::Cancel')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</FormContainer>
|
|
|
|
|
|
</Form>
|
|
|
|
|
|
)
|
|
|
|
|
|
}}
|
|
|
|
|
|
</Formik>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default OrganizationUnits
|