erp-platform/ui/src/views/admin/organization-unit/OrganizationUnits.tsx

1077 lines
33 KiB
TypeScript
Raw Normal View History

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'
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,
} 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>,
{
placement: 'top-end',
2025-05-06 06:45:49 +00:00
},
)
fetchData()
onDialogClose()
} catch (error) {
toast.push(
<Notification type="danger" duration={2000}>
Hata
</Notification>,
{
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>,
{
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>,
{
placement: 'top-end',
2025-05-06 06:45:49 +00:00
},
)
fetchData()
} catch (error) {
toast.push(
<Notification type="danger" duration={2000}>
Hata
</Notification>,
{
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>,
{
placement: 'top-end',
2025-05-06 06:45:49 +00:00
},
)
fetchData()
} catch (error) {
toast.push(
<Notification type="danger" duration={2000}>
Hata
</Notification>,
{
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">
<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>
<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' })}
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([])}
>
<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>,
{
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>,
{
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([])}
>
<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>,
{
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>,
{
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)}
>
<h5 className="mb-4">{translate('::Delete')}</h5>
2025-05-06 06:45:49 +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)
}}
>
{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>,
{
placement: 'top-end',
2025-05-06 06:45:49 +00:00
},
)
} catch (error) {
toast.push(
<Notification type="danger" duration={2000}>
Hata
</Notification>,
{
placement: 'top-end',
2025-05-06 06:45:49 +00:00
},
)
} finally {
setLoading(false)
}
}}
>
{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>,
{
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>,
{
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