ajout app
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||
import {
|
||||
Button,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Portal,
|
||||
Table,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useToast,
|
||||
} from '@chakra-ui/react'
|
||||
import cx from 'classnames'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import InvitationAPI, { SortBy, SortOrder } from '@/client/api/invitation'
|
||||
import OrganizationAPI from '@/client/api/organization'
|
||||
import { geEditorPermission } from '@/client/api/permission'
|
||||
import { swrConfig } from '@/client/options'
|
||||
import OrganizationInviteMembers from '@/components/organization/organization-invite-members'
|
||||
import OrganizationStatus from '@/components/organization/organization-status'
|
||||
import prettyDate from '@/helpers/pretty-date'
|
||||
import { outgoingInvitationPaginationStorage } from '@/infra/pagination'
|
||||
import {
|
||||
IconMoreVert,
|
||||
IconSend,
|
||||
IconDelete,
|
||||
IconPersonAdd,
|
||||
SectionSpinner,
|
||||
PagePagination,
|
||||
usePagePagination,
|
||||
} from '@/lib'
|
||||
|
||||
const OrganizationInvitationsPage = () => {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const { id } = useParams()
|
||||
const toast = useToast()
|
||||
const { data: org, error: orgError } = OrganizationAPI.useGetById(
|
||||
id,
|
||||
swrConfig(),
|
||||
)
|
||||
const { page, size, steps, setPage, setSize } = usePagePagination({
|
||||
navigate,
|
||||
location,
|
||||
storage: outgoingInvitationPaginationStorage(),
|
||||
})
|
||||
const {
|
||||
data: list,
|
||||
error: invitationsError,
|
||||
mutate,
|
||||
} = InvitationAPI.useGetOutgoing(
|
||||
{
|
||||
organizationId: id,
|
||||
page,
|
||||
size,
|
||||
sortBy: SortBy.DateCreated,
|
||||
sortOrder: SortOrder.Desc,
|
||||
},
|
||||
swrConfig(),
|
||||
)
|
||||
const [isInviteMembersModalOpen, setIsInviteMembersModalOpen] =
|
||||
useState(false)
|
||||
|
||||
const handleResend = useCallback(
|
||||
async (invitationId: string) => {
|
||||
await InvitationAPI.resend(invitationId)
|
||||
toast({
|
||||
title: 'Invitation resent',
|
||||
status: 'success',
|
||||
isClosable: true,
|
||||
})
|
||||
},
|
||||
[toast],
|
||||
)
|
||||
|
||||
const handleDelete = useCallback(
|
||||
async (invitationId: string) => {
|
||||
await InvitationAPI.delete(invitationId)
|
||||
mutate()
|
||||
},
|
||||
[mutate],
|
||||
)
|
||||
|
||||
if (invitationsError || orgError) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!list || !org) {
|
||||
return <SectionSpinner />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{org.name}</title>
|
||||
</Helmet>
|
||||
{list && list.data.length === 0 ? (
|
||||
<>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'h-[300px]',
|
||||
)}
|
||||
>
|
||||
<div className={cx('flex', 'flex-col', 'gap-1.5', 'items-center')}>
|
||||
<span>This organization has no invitations.</span>
|
||||
{geEditorPermission(org.permission) && (
|
||||
<Button
|
||||
leftIcon={<IconPersonAdd />}
|
||||
onClick={() => {
|
||||
setIsInviteMembersModalOpen(true)
|
||||
}}
|
||||
>
|
||||
Invite Members
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<OrganizationInviteMembers
|
||||
open={isInviteMembersModalOpen}
|
||||
id={org.id}
|
||||
onClose={() => setIsInviteMembersModalOpen(false)}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
{list && list.data.length > 0 ? (
|
||||
<div className={cx('flex', 'flex-col', 'gap-3.5', 'py-3.5')}>
|
||||
<Table variant="simple">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Email</Th>
|
||||
<Th>Status</Th>
|
||||
<Th>Date</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{list.data.map((i) => (
|
||||
<Tr key={i.id}>
|
||||
<Td>{i.email}</Td>
|
||||
<Td>
|
||||
<OrganizationStatus value={i.status} />
|
||||
</Td>
|
||||
<Td>{prettyDate(i.createTime)}</Td>
|
||||
<Td className={cx('text-right')}>
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
icon={<IconMoreVert />}
|
||||
variant="ghost"
|
||||
aria-label=""
|
||||
/>
|
||||
<Portal>
|
||||
<MenuList>
|
||||
{i.status === 'pending' && (
|
||||
<MenuItem
|
||||
icon={<IconSend />}
|
||||
onClick={() => handleResend(i.id)}
|
||||
>
|
||||
Resend
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
icon={<IconDelete />}
|
||||
className={cx('text-red-500')}
|
||||
onClick={() => handleDelete(i.id)}
|
||||
>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Portal>
|
||||
</Menu>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
{list && (
|
||||
<PagePagination
|
||||
style={{ alignSelf: 'end' }}
|
||||
totalElements={list.totalElements}
|
||||
totalPages={list.totalPages}
|
||||
page={page}
|
||||
size={size}
|
||||
steps={steps}
|
||||
setPage={setPage}
|
||||
setSize={setSize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrganizationInvitationsPage
|
||||
56
Voltaserve/ui/src/pages/organization/organization-layout.tsx
Normal file
56
Voltaserve/ui/src/pages/organization/organization-layout.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||
import { Heading, Tab, TabList, Tabs } from '@chakra-ui/react'
|
||||
import cx from 'classnames'
|
||||
import OrganizationAPI from '@/client/api/organization'
|
||||
import { geOwnerPermission } from '@/client/api/permission'
|
||||
import { swrConfig } from '@/client/options'
|
||||
|
||||
const OrganizationLayout = () => {
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const { id } = useParams()
|
||||
const { data: org } = OrganizationAPI.useGetById(id, swrConfig())
|
||||
const [tabIndex, setTabIndex] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
const segments = location.pathname.split('/')
|
||||
const segment = segments[segments.length - 1]
|
||||
if (segment === 'member') {
|
||||
setTabIndex(0)
|
||||
} else if (segment === 'invitation') {
|
||||
setTabIndex(1)
|
||||
} else if (segment === 'settings') {
|
||||
setTabIndex(2)
|
||||
}
|
||||
}, [location])
|
||||
|
||||
if (!org) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx('flex', 'flex-col', 'gap-3.5')}>
|
||||
<Heading className={cx('text-heading')}>{org.name}</Heading>
|
||||
<Tabs variant="solid-rounded" colorScheme="gray" index={tabIndex}>
|
||||
<TabList>
|
||||
<Tab onClick={() => navigate(`/organization/${id}/member`)}>
|
||||
Members
|
||||
</Tab>
|
||||
<Tab
|
||||
onClick={() => navigate(`/organization/${id}/invitation`)}
|
||||
display={geOwnerPermission(org.permission) ? 'auto' : 'none'}
|
||||
>
|
||||
Invitations
|
||||
</Tab>
|
||||
<Tab onClick={() => navigate(`/organization/${id}/settings`)}>
|
||||
Settings
|
||||
</Tab>
|
||||
</TabList>
|
||||
</Tabs>
|
||||
<Outlet />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrganizationLayout
|
||||
149
Voltaserve/ui/src/pages/organization/organization-list-page.tsx
Normal file
149
Voltaserve/ui/src/pages/organization/organization-list-page.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
import { useEffect } from 'react'
|
||||
import {
|
||||
Link,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useSearchParams,
|
||||
} from 'react-router-dom'
|
||||
import {
|
||||
Heading,
|
||||
Link as ChakraLink,
|
||||
Table,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
Avatar,
|
||||
Badge,
|
||||
} from '@chakra-ui/react'
|
||||
import cx from 'classnames'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import OrganizationAPI, { SortOrder } from '@/client/api/organization'
|
||||
import { swrConfig } from '@/client/options'
|
||||
import { CreateOrganizationButton } from '@/components/top-bar/top-bar-buttons'
|
||||
import prettyDate from '@/helpers/pretty-date'
|
||||
import { decodeQuery } from '@/helpers/query'
|
||||
import { organizationPaginationStorage } from '@/infra/pagination'
|
||||
import { SectionSpinner, PagePagination, usePagePagination } from '@/lib'
|
||||
|
||||
const OrganizationListPage = () => {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const [searchParams] = useSearchParams()
|
||||
const query = decodeQuery(searchParams.get('q') as string)
|
||||
const { page, size, steps, setPage, setSize } = usePagePagination({
|
||||
navigate,
|
||||
location,
|
||||
storage: organizationPaginationStorage(),
|
||||
})
|
||||
const {
|
||||
data: list,
|
||||
error,
|
||||
mutate,
|
||||
} = OrganizationAPI.useList(
|
||||
{ query, page, size, sortOrder: SortOrder.Desc },
|
||||
swrConfig(),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
mutate()
|
||||
}, [query, page, size, mutate])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Organizations</title>
|
||||
</Helmet>
|
||||
<div className={cx('flex', 'flex-col', 'gap-3.5', 'pb-3.5')}>
|
||||
<Heading className={cx('pl-2', 'text-heading')}>Organizations</Heading>
|
||||
{!list && error && (
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'h-[300px]',
|
||||
)}
|
||||
>
|
||||
<span>Failed to load organizations.</span>
|
||||
</div>
|
||||
)}
|
||||
{!list && !error && <SectionSpinner />}
|
||||
{list && list.data.length === 0 && (
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'h-[300px]',
|
||||
)}
|
||||
>
|
||||
<div className={cx('flex', 'flex-col', 'gap-1.5', 'items-center')}>
|
||||
<span>There are no organizations.</span>
|
||||
<CreateOrganizationButton />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{list && list.data.length > 0 && (
|
||||
<Table variant="simple">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Name</Th>
|
||||
<Th>Permission</Th>
|
||||
<Th>Date</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{list.data.map((o) => (
|
||||
<Tr key={o.id}>
|
||||
<Td>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'gap-1.5',
|
||||
'items-center',
|
||||
)}
|
||||
>
|
||||
<Avatar
|
||||
name={o.name}
|
||||
size="sm"
|
||||
className={cx('w-[40px]', 'h-[40px]')}
|
||||
/>
|
||||
<ChakraLink
|
||||
as={Link}
|
||||
to={`/organization/${o.id}/member`}
|
||||
className={cx('no-underline')}
|
||||
>
|
||||
<span>{o.name}</span>
|
||||
</ChakraLink>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<Badge>{o.permission}</Badge>
|
||||
</Td>
|
||||
<Td>{prettyDate(o.createTime)}</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
)}
|
||||
{list && (
|
||||
<PagePagination
|
||||
style={{ alignSelf: 'end' }}
|
||||
totalElements={list.totalElements}
|
||||
totalPages={list.totalPages}
|
||||
page={page}
|
||||
size={size}
|
||||
steps={steps}
|
||||
setPage={setPage}
|
||||
setSize={setSize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrganizationListPage
|
||||
@@ -0,0 +1,211 @@
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useParams,
|
||||
useSearchParams,
|
||||
} from 'react-router-dom'
|
||||
import {
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Table,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
Button,
|
||||
Avatar,
|
||||
Portal,
|
||||
} from '@chakra-ui/react'
|
||||
import cx from 'classnames'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import OrganizationAPI from '@/client/api/organization'
|
||||
import { geEditorPermission } from '@/client/api/permission'
|
||||
import UserAPI, { SortBy, SortOrder, User } from '@/client/api/user'
|
||||
import { swrConfig } from '@/client/options'
|
||||
import OrganizationInviteMembers from '@/components/organization/organization-invite-members'
|
||||
import OrganizationRemoveMember from '@/components/organization/organization-remove-member'
|
||||
import { decodeQuery } from '@/helpers/query'
|
||||
import { organizationMemberPaginationStorage } from '@/infra/pagination'
|
||||
import {
|
||||
IconMoreVert,
|
||||
IconLogout,
|
||||
IconPersonAdd,
|
||||
SectionSpinner,
|
||||
PagePagination,
|
||||
usePagePagination,
|
||||
} from '@/lib'
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hook'
|
||||
import {
|
||||
inviteModalDidClose,
|
||||
inviteModalDidOpen,
|
||||
} from '@/store/ui/organizations'
|
||||
|
||||
const OrganizationMembersPage = () => {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const dispatch = useAppDispatch()
|
||||
const { id } = useParams()
|
||||
const { data: org, error: orgError } = OrganizationAPI.useGetById(
|
||||
id,
|
||||
swrConfig(),
|
||||
)
|
||||
const { page, size, steps, setPage, setSize } = usePagePagination({
|
||||
navigate,
|
||||
location,
|
||||
storage: organizationMemberPaginationStorage(),
|
||||
})
|
||||
const [searchParams] = useSearchParams()
|
||||
const query = decodeQuery(searchParams.get('q') as string)
|
||||
const {
|
||||
data: list,
|
||||
error: membersError,
|
||||
mutate,
|
||||
} = UserAPI.useList(
|
||||
{
|
||||
query,
|
||||
organizationId: id,
|
||||
page,
|
||||
size,
|
||||
sortBy: SortBy.FullName,
|
||||
sortOrder: SortOrder.Asc,
|
||||
},
|
||||
swrConfig(),
|
||||
)
|
||||
const isInviteMembersModalOpen = useAppSelector(
|
||||
(state) => state.ui.organizations.isInviteModalOpen,
|
||||
)
|
||||
const [userToRemove, setUserToRemove] = useState<User>()
|
||||
const [isRemoveMemberModalOpen, setIsRemoveMemberModalOpen] =
|
||||
useState<boolean>(false)
|
||||
|
||||
if (membersError || orgError) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!list || !org) {
|
||||
return <SectionSpinner />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{org.name}</title>
|
||||
</Helmet>
|
||||
{list.data.length > 0 && (
|
||||
<div className={cx('flex', 'flex-col', 'gap-3.5', 'pb-3.5')}>
|
||||
<Table variant="simple">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Full name</Th>
|
||||
<Th>Email</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{list.data.map((u) => (
|
||||
<Tr key={u.id}>
|
||||
<Td>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'gap-1.5',
|
||||
'items-center',
|
||||
)}
|
||||
>
|
||||
<Avatar name={u.fullName} src={u.picture} />
|
||||
<span>{u.fullName}</span>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>{u.email}</Td>
|
||||
<Td className={cx('text-right')}>
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
icon={<IconMoreVert />}
|
||||
variant="ghost"
|
||||
aria-label=""
|
||||
/>
|
||||
<Portal>
|
||||
<MenuList>
|
||||
<MenuItem
|
||||
icon={<IconLogout />}
|
||||
className={cx('text-red-500')}
|
||||
isDisabled={!geEditorPermission(org.permission)}
|
||||
onClick={() => {
|
||||
setUserToRemove(u)
|
||||
setIsRemoveMemberModalOpen(true)
|
||||
}}
|
||||
>
|
||||
Remove From Organization
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Portal>
|
||||
</Menu>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
{list && (
|
||||
<PagePagination
|
||||
style={{ alignSelf: 'end' }}
|
||||
totalElements={list.totalElements}
|
||||
totalPages={list.totalPages}
|
||||
page={page}
|
||||
size={size}
|
||||
steps={steps}
|
||||
setPage={setPage}
|
||||
setSize={setSize}
|
||||
/>
|
||||
)}
|
||||
{userToRemove && (
|
||||
<OrganizationRemoveMember
|
||||
isOpen={isRemoveMemberModalOpen}
|
||||
user={userToRemove}
|
||||
organization={org}
|
||||
onCompleted={() => mutate()}
|
||||
onClose={() => setIsRemoveMemberModalOpen(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{list.data.length === 0 && (
|
||||
<>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'h-[300px]',
|
||||
)}
|
||||
>
|
||||
<div className={cx('flex', 'flex-col', 'gap-1.5', 'items-center')}>
|
||||
<span>This organization has no members.</span>
|
||||
{geEditorPermission(org.permission) && (
|
||||
<Button
|
||||
leftIcon={<IconPersonAdd />}
|
||||
onClick={() => dispatch(inviteModalDidOpen())}
|
||||
>
|
||||
Invite Members
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<OrganizationInviteMembers
|
||||
open={isInviteMembersModalOpen}
|
||||
id={org.id}
|
||||
onClose={() => dispatch(inviteModalDidClose())}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrganizationMembersPage
|
||||
@@ -0,0 +1,129 @@
|
||||
import { useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Divider, IconButton } from '@chakra-ui/react'
|
||||
import cx from 'classnames'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import OrganizationAPI from '@/client/api/organization'
|
||||
import { geEditorPermission, geOwnerPermission } from '@/client/api/permission'
|
||||
import { swrConfig } from '@/client/options'
|
||||
import OrganizationDelete from '@/components/organization/organization-delete'
|
||||
import OrganizationEditName from '@/components/organization/organization-edit-name'
|
||||
import OrganizationInviteMembers from '@/components/organization/organization-invite-members'
|
||||
import OrganizationLeave from '@/components/organization/organization-leave'
|
||||
import {
|
||||
IconEdit,
|
||||
IconLogout,
|
||||
IconDelete,
|
||||
IconPersonAdd,
|
||||
SectionSpinner,
|
||||
} from '@/lib'
|
||||
|
||||
const Spacer = () => <div className={cx('grow')} />
|
||||
|
||||
const OrganizationSettingsPage = () => {
|
||||
const { id } = useParams()
|
||||
const { data: org, error } = OrganizationAPI.useGetById(id, swrConfig())
|
||||
const [isNameModalOpen, setIsNameModalOpen] = useState(false)
|
||||
const [isInviteMembersModalOpen, setIsInviteMembersModalOpen] =
|
||||
useState(false)
|
||||
const [isLeaveModalOpen, setIsLeaveModalOpen] = useState(false)
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
|
||||
const sectionClassName = cx('flex', 'flex-col', 'gap-1', 'py-1.5')
|
||||
const rowClassName = cx(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'gap-1',
|
||||
`h-[40px]`,
|
||||
)
|
||||
|
||||
if (error) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!org) {
|
||||
return <SectionSpinner />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{org.name}</title>
|
||||
</Helmet>
|
||||
<div className={sectionClassName}>
|
||||
<div className={rowClassName}>
|
||||
<span>Name</span>
|
||||
<Spacer />
|
||||
<span>{org.name}</span>
|
||||
<IconButton
|
||||
icon={<IconEdit />}
|
||||
isDisabled={!geEditorPermission(org.permission)}
|
||||
aria-label=""
|
||||
onClick={() => {
|
||||
setIsNameModalOpen(true)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
<div className={rowClassName}>
|
||||
<span>Invite members</span>
|
||||
<Spacer />
|
||||
<IconButton
|
||||
icon={<IconPersonAdd />}
|
||||
isDisabled={!geOwnerPermission(org.permission)}
|
||||
aria-label=""
|
||||
onClick={() => {
|
||||
setIsInviteMembersModalOpen(true)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={rowClassName}>
|
||||
<span>Leave</span>
|
||||
<Spacer />
|
||||
<IconButton
|
||||
icon={<IconLogout />}
|
||||
variant="solid"
|
||||
colorScheme="red"
|
||||
aria-label=""
|
||||
onClick={() => setIsLeaveModalOpen(true)}
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
<div className={rowClassName}>
|
||||
<span>Delete permanently</span>
|
||||
<Spacer />
|
||||
<IconButton
|
||||
icon={<IconDelete />}
|
||||
variant="solid"
|
||||
colorScheme="red"
|
||||
isDisabled={!geEditorPermission(org.permission)}
|
||||
aria-label=""
|
||||
onClick={() => setIsDeleteModalOpen(true)}
|
||||
/>
|
||||
</div>
|
||||
<OrganizationEditName
|
||||
open={isNameModalOpen}
|
||||
organization={org}
|
||||
onClose={() => setIsNameModalOpen(false)}
|
||||
/>
|
||||
<OrganizationInviteMembers
|
||||
open={isInviteMembersModalOpen}
|
||||
id={org.id}
|
||||
onClose={() => setIsInviteMembersModalOpen(false)}
|
||||
/>
|
||||
<OrganizationLeave
|
||||
open={isLeaveModalOpen}
|
||||
id={org.id}
|
||||
onClose={() => setIsLeaveModalOpen(false)}
|
||||
/>
|
||||
<OrganizationDelete
|
||||
open={isDeleteModalOpen}
|
||||
organization={org}
|
||||
onClose={() => setIsDeleteModalOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrganizationSettingsPage
|
||||
Reference in New Issue
Block a user