|
|
@ -4,7 +4,7 @@ import { |
|
|
|
Container, |
|
|
|
Flex, |
|
|
|
Heading, |
|
|
|
Spinner, |
|
|
|
SkeletonText, |
|
|
|
Table, |
|
|
|
TableContainer, |
|
|
|
Tbody, |
|
|
@ -13,106 +13,103 @@ import { |
|
|
|
Thead, |
|
|
|
Tr, |
|
|
|
} from "@chakra-ui/react" |
|
|
|
import { useQuery, useQueryClient } from "@tanstack/react-query" |
|
|
|
import { useQueryClient, useSuspenseQuery } from "@tanstack/react-query" |
|
|
|
import { createFileRoute } from "@tanstack/react-router" |
|
|
|
|
|
|
|
import { Suspense } from "react" |
|
|
|
import { type UserPublic, UsersService } from "../../client" |
|
|
|
import ActionsMenu from "../../components/Common/ActionsMenu" |
|
|
|
import Navbar from "../../components/Common/Navbar" |
|
|
|
import useCustomToast from "../../hooks/useCustomToast" |
|
|
|
|
|
|
|
export const Route = createFileRoute("/_layout/admin")({ |
|
|
|
component: Admin, |
|
|
|
}) |
|
|
|
|
|
|
|
function Admin() { |
|
|
|
const MembersTableBody = () => { |
|
|
|
const queryClient = useQueryClient() |
|
|
|
const showToast = useCustomToast() |
|
|
|
const currentUser = queryClient.getQueryData<UserPublic>(["currentUser"]) |
|
|
|
const { |
|
|
|
data: users, |
|
|
|
isLoading, |
|
|
|
isError, |
|
|
|
error, |
|
|
|
} = useQuery({ |
|
|
|
|
|
|
|
const { data: users } = useSuspenseQuery({ |
|
|
|
queryKey: ["users"], |
|
|
|
queryFn: () => UsersService.readUsers({}), |
|
|
|
}) |
|
|
|
|
|
|
|
if (isError) { |
|
|
|
const errDetail = (error as any).body?.detail |
|
|
|
showToast("Something went wrong.", `${errDetail}`, "error") |
|
|
|
} |
|
|
|
return ( |
|
|
|
<Tbody> |
|
|
|
{users.data.map((user) => ( |
|
|
|
<Tr key={user.id}> |
|
|
|
<Td color={!user.full_name ? "ui.dim" : "inherit"}> |
|
|
|
{user.full_name || "N/A"} |
|
|
|
{currentUser?.id === user.id && ( |
|
|
|
<Badge ml="1" colorScheme="teal"> |
|
|
|
You |
|
|
|
</Badge> |
|
|
|
)} |
|
|
|
</Td> |
|
|
|
<Td>{user.email}</Td> |
|
|
|
<Td>{user.is_superuser ? "Superuser" : "User"}</Td> |
|
|
|
<Td> |
|
|
|
<Flex gap={2}> |
|
|
|
<Box |
|
|
|
w="2" |
|
|
|
h="2" |
|
|
|
borderRadius="50%" |
|
|
|
bg={user.is_active ? "ui.success" : "ui.danger"} |
|
|
|
alignSelf="center" |
|
|
|
/> |
|
|
|
{user.is_active ? "Active" : "Inactive"} |
|
|
|
</Flex> |
|
|
|
</Td> |
|
|
|
<Td> |
|
|
|
<ActionsMenu |
|
|
|
type="User" |
|
|
|
value={user} |
|
|
|
disabled={currentUser?.id === user.id ? true : false} |
|
|
|
/> |
|
|
|
</Td> |
|
|
|
</Tr> |
|
|
|
))} |
|
|
|
</Tbody> |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
const MembersBodySkeleton = () => { |
|
|
|
return ( |
|
|
|
<Tbody> |
|
|
|
<Tr> |
|
|
|
{new Array(5).fill(null).map((_, index) => ( |
|
|
|
<Td key={index}> |
|
|
|
<SkeletonText noOfLines={1} paddingBlock="16px" /> |
|
|
|
</Td> |
|
|
|
))} |
|
|
|
</Tr> |
|
|
|
</Tbody> |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
function Admin() { |
|
|
|
return ( |
|
|
|
<> |
|
|
|
{isLoading ? ( |
|
|
|
// TODO: Add skeleton
|
|
|
|
<Flex justify="center" align="center" height="100vh" width="full"> |
|
|
|
<Spinner size="xl" color="ui.main" /> |
|
|
|
</Flex> |
|
|
|
) : ( |
|
|
|
users && ( |
|
|
|
<Container maxW="full"> |
|
|
|
<Heading |
|
|
|
size="lg" |
|
|
|
textAlign={{ base: "center", md: "left" }} |
|
|
|
pt={12} |
|
|
|
> |
|
|
|
User Management |
|
|
|
</Heading> |
|
|
|
<Navbar type={"User"} /> |
|
|
|
<TableContainer> |
|
|
|
<Table fontSize="md" size={{ base: "sm", md: "md" }}> |
|
|
|
<Thead> |
|
|
|
<Tr> |
|
|
|
<Th>Full name</Th> |
|
|
|
<Th>Email</Th> |
|
|
|
<Th>Role</Th> |
|
|
|
<Th>Status</Th> |
|
|
|
<Th>Actions</Th> |
|
|
|
</Tr> |
|
|
|
</Thead> |
|
|
|
<Tbody> |
|
|
|
{users.data.map((user) => ( |
|
|
|
<Tr key={user.id}> |
|
|
|
<Td color={!user.full_name ? "ui.dim" : "inherit"}> |
|
|
|
{user.full_name || "N/A"} |
|
|
|
{currentUser?.id === user.id && ( |
|
|
|
<Badge ml="1" colorScheme="teal"> |
|
|
|
You |
|
|
|
</Badge> |
|
|
|
)} |
|
|
|
</Td> |
|
|
|
<Td>{user.email}</Td> |
|
|
|
<Td>{user.is_superuser ? "Superuser" : "User"}</Td> |
|
|
|
<Td> |
|
|
|
<Flex gap={2}> |
|
|
|
<Box |
|
|
|
w="2" |
|
|
|
h="2" |
|
|
|
borderRadius="50%" |
|
|
|
bg={user.is_active ? "ui.success" : "ui.danger"} |
|
|
|
alignSelf="center" |
|
|
|
/> |
|
|
|
{user.is_active ? "Active" : "Inactive"} |
|
|
|
</Flex> |
|
|
|
</Td> |
|
|
|
<Td> |
|
|
|
<ActionsMenu |
|
|
|
type="User" |
|
|
|
value={user} |
|
|
|
disabled={currentUser?.id === user.id ? true : false} |
|
|
|
/> |
|
|
|
</Td> |
|
|
|
</Tr> |
|
|
|
))} |
|
|
|
</Tbody> |
|
|
|
</Table> |
|
|
|
</TableContainer> |
|
|
|
</Container> |
|
|
|
) |
|
|
|
)} |
|
|
|
</> |
|
|
|
<Container maxW="full"> |
|
|
|
<Heading size="lg" textAlign={{ base: "center", md: "left" }} pt={12}> |
|
|
|
User Management |
|
|
|
</Heading> |
|
|
|
<Navbar type={"User"} /> |
|
|
|
<TableContainer> |
|
|
|
<Table fontSize="md" size={{ base: "sm", md: "md" }}> |
|
|
|
<Thead> |
|
|
|
<Tr> |
|
|
|
<Th width="20%">Full name</Th> |
|
|
|
<Th width="50%">Email</Th> |
|
|
|
<Th width="10%">Role</Th> |
|
|
|
<Th width="10%">Status</Th> |
|
|
|
<Th width="10%">Actions</Th> |
|
|
|
</Tr> |
|
|
|
</Thead> |
|
|
|
<Suspense fallback={<MembersBodySkeleton />}> |
|
|
|
<MembersTableBody /> |
|
|
|
</Suspense> |
|
|
|
</Table> |
|
|
|
</TableContainer> |
|
|
|
</Container> |
|
|
|
) |
|
|
|
} |
|
|
|