committed by
GitHub
20 changed files with 927 additions and 81 deletions
@ -0,0 +1,39 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Button, Menu, MenuButton, MenuItem, MenuList, useDisclosure } from '@chakra-ui/react'; |
||||
|
import { BsThreeDotsVertical } from 'react-icons/bs'; |
||||
|
import { FiTrash, FiEdit } from 'react-icons/fi'; |
||||
|
|
||||
|
import Delete from '../pages/modals/DeleteAlert'; |
||||
|
import EditUser from '../pages/modals/EditUser'; |
||||
|
import EditItem from '../pages/modals/EditItem'; |
||||
|
|
||||
|
interface ActionsMenuProps { |
||||
|
type: string; |
||||
|
id: number; |
||||
|
} |
||||
|
|
||||
|
const ActionsMenu: React.FC<ActionsMenuProps> = ({ type, id }) => { |
||||
|
const editUserModal = useDisclosure(); |
||||
|
const deleteModal = useDisclosure(); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Menu> |
||||
|
<MenuButton as={Button} bg="white" rightIcon={<BsThreeDotsVertical />} variant="unstyled"> |
||||
|
</MenuButton> |
||||
|
<MenuList> |
||||
|
<MenuItem onClick={editUserModal.onOpen} icon={<FiEdit fontSize="16px" />}>Edit {type}</MenuItem> |
||||
|
<MenuItem onClick={deleteModal.onOpen} icon={<FiTrash fontSize="16px" />} color="ui.danger">Delete {type}</MenuItem> |
||||
|
</MenuList> |
||||
|
{ |
||||
|
type === "User" ? <EditUser isOpen={editUserModal.isOpen} onClose={editUserModal.onClose} /> |
||||
|
: <EditItem isOpen={editUserModal.isOpen} onClose={editUserModal.onClose} /> |
||||
|
} |
||||
|
<Delete type={type} id={id} isOpen={deleteModal.isOpen} onClose={deleteModal.onClose} /> |
||||
|
</Menu> |
||||
|
</> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default ActionsMenu; |
@ -0,0 +1,30 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Button, Flex, Icon, useDisclosure } from '@chakra-ui/react'; |
||||
|
import { FaPlus } from "react-icons/fa"; |
||||
|
|
||||
|
import CreateItem from '../pages/modals/CreateItem'; |
||||
|
import CreateUser from '../pages/modals/CreateUser'; |
||||
|
|
||||
|
interface NavbarProps { |
||||
|
type: string; |
||||
|
} |
||||
|
|
||||
|
const Navbar: React.FC<NavbarProps> = ({ type }) => { |
||||
|
const createUserModal = useDisclosure(); |
||||
|
const createItemModal = useDisclosure(); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Flex gap={4} py={{ base: "8", md: "4" }} justify={{ base: "center", md: "end" }}> |
||||
|
<Button bg="ui.main" color="white" gap={1} fontSize={{ base: "sm", md: "inherit" }} onClick={type === "User" ? createUserModal.onOpen : createItemModal.onOpen}> |
||||
|
<Icon as={FaPlus} /> Create {type} |
||||
|
</Button> |
||||
|
<CreateUser isOpen={createUserModal.isOpen} onClose={createUserModal.onClose} /> |
||||
|
<CreateItem isOpen={createItemModal.isOpen} onClose={createItemModal.onClose} /> |
||||
|
</Flex> |
||||
|
</> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Navbar; |
@ -0,0 +1,89 @@ |
|||||
|
import React, { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { Box, Container, Flex, Heading, Spinner, Table, TableContainer, Tbody, Td, Th, Thead, Tr, useToast } from '@chakra-ui/react'; |
||||
|
|
||||
|
import ActionsMenu from '../../components/ActionsMenu'; |
||||
|
import Navbar from '../../components/Navbar'; |
||||
|
import { useUsersStore } from '../../store/users-store'; |
||||
|
|
||||
|
const Admin: React.FC = () => { |
||||
|
const toast = useToast(); |
||||
|
const [isLoading, setIsLoading] = useState(false); |
||||
|
const { users, getUsers } = useUsersStore(); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const fetchUsers = async () => { |
||||
|
try { |
||||
|
setIsLoading(true); |
||||
|
await getUsers(); |
||||
|
setIsLoading(false); |
||||
|
} catch (err) { |
||||
|
setIsLoading(false); |
||||
|
toast({ |
||||
|
title: 'Something went wrong.', |
||||
|
description: 'Failed to fetch users. Please try again.', |
||||
|
status: 'error', |
||||
|
isClosable: true, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
fetchUsers(); |
||||
|
}, []) |
||||
|
|
||||
|
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" color="gray.700" 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> |
||||
|
</Tr> |
||||
|
</Thead> |
||||
|
<Tbody> |
||||
|
{users.map((user) => ( |
||||
|
<Tr key={user.id}> |
||||
|
<Td color={!user.full_name ? "gray.600" : "inherit"}>{user.full_name || "N/A"}</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" id={user.id} /> |
||||
|
</Td> |
||||
|
</Tr> |
||||
|
))} |
||||
|
</Tbody> |
||||
|
</Table> |
||||
|
</TableContainer> |
||||
|
</Container> |
||||
|
)} |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Admin; |
@ -0,0 +1,24 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Box, Text } from '@chakra-ui/react'; |
||||
|
|
||||
|
import { useUserStore } from '../../store/user-store'; |
||||
|
|
||||
|
|
||||
|
const Dashboard: React.FC = () => { |
||||
|
const { user } = useUserStore(); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
{user ? ( |
||||
|
<Box width="100%" p={8}> |
||||
|
<Text fontSize="24px">Hi, {user.full_name || user.email} 👋🏼</Text> |
||||
|
<Text>Welcome back, nice to see you again!</Text> |
||||
|
</Box> |
||||
|
) : null} |
||||
|
</> |
||||
|
|
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Dashboard; |
@ -0,0 +1,78 @@ |
|||||
|
import React, { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { Container, Flex, Heading, Spinner, Table, TableContainer, Tbody, Td, Th, Thead, Tr, useToast } from '@chakra-ui/react'; |
||||
|
|
||||
|
import ActionsMenu from '../../components/ActionsMenu'; |
||||
|
import Navbar from '../../components/Navbar'; |
||||
|
import { useItemsStore } from '../../store/items-store'; |
||||
|
|
||||
|
|
||||
|
const Items: React.FC = () => { |
||||
|
const toast = useToast(); |
||||
|
const [isLoading, setIsLoading] = useState(false); |
||||
|
const { items, getItems } = useItemsStore(); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const fetchItems = async () => { |
||||
|
try { |
||||
|
setIsLoading(true); |
||||
|
await getItems(); |
||||
|
setIsLoading(false); |
||||
|
} catch (err) { |
||||
|
setIsLoading(false); |
||||
|
toast({ |
||||
|
title: 'Something went wrong.', |
||||
|
description: 'Failed to fetch items. Please try again.', |
||||
|
status: 'error', |
||||
|
isClosable: true, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
fetchItems(); |
||||
|
}, []) |
||||
|
|
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
{isLoading ? ( |
||||
|
// TODO: Add skeleton
|
||||
|
<Flex justify="center" align="center" height="100vh" width="full"> |
||||
|
<Spinner size="xl" color='ui.main' /> |
||||
|
</Flex> |
||||
|
) : ( |
||||
|
items && |
||||
|
<Container maxW="full"> |
||||
|
<Heading size="lg" color="gray.700" textAlign={{ base: "center", md: "left" }} pt={12}> |
||||
|
Items Management |
||||
|
</Heading> |
||||
|
<Navbar type={"Item"} /> |
||||
|
<TableContainer> |
||||
|
<Table size={{ base: "sm", md: "md" }}> |
||||
|
<Thead> |
||||
|
<Tr> |
||||
|
<Th>ID</Th> |
||||
|
<Th>Title</Th> |
||||
|
<Th>Description</Th> |
||||
|
</Tr> |
||||
|
</Thead> |
||||
|
<Tbody> |
||||
|
{items.map((item) => ( |
||||
|
<Tr key={item.id}> |
||||
|
<Td>{item.id}</Td> |
||||
|
<Td>{item.title}</Td> |
||||
|
<Td color={!item.description ? "gray.600" : "inherit"}>{item.description || "N/A"}</Td> |
||||
|
<Td> |
||||
|
<ActionsMenu type={"Item"} id={item.id} /> |
||||
|
</Td> |
||||
|
</Tr> |
||||
|
))} |
||||
|
</Tbody> |
||||
|
</Table> |
||||
|
</TableContainer> |
||||
|
</Container> |
||||
|
)} |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Items; |
@ -0,0 +1,49 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Container, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react'; |
||||
|
|
||||
|
import Appearance from '../panels/Appearance'; |
||||
|
import ChangePassword from '../panels/ChangePassword'; |
||||
|
import DeleteAccount from '../panels/DeleteAccount'; |
||||
|
import UserInformation from '../panels/UserInformation'; |
||||
|
|
||||
|
|
||||
|
|
||||
|
const Profile: React.FC = () => { |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Container maxW="full"> |
||||
|
<Heading size="lg" color="gray.700" textAlign={{ base: "center", md: "left" }} py={12}> |
||||
|
User Settings |
||||
|
</Heading> |
||||
|
<Tabs variant='enclosed' > |
||||
|
<TabList> |
||||
|
<Tab>Profile</Tab> |
||||
|
<Tab>Password</Tab> |
||||
|
<Tab>Appearance</Tab> |
||||
|
<Tab>Danger zone</Tab> |
||||
|
</TabList> |
||||
|
<TabPanels> |
||||
|
<TabPanel> |
||||
|
<UserInformation /> |
||||
|
</TabPanel> |
||||
|
<TabPanel> |
||||
|
<ChangePassword /> |
||||
|
</TabPanel> |
||||
|
<TabPanel> |
||||
|
<Appearance /> |
||||
|
</TabPanel> |
||||
|
<TabPanel> |
||||
|
<DeleteAccount /> |
||||
|
</TabPanel> |
||||
|
|
||||
|
</TabPanels> |
||||
|
</Tabs> |
||||
|
</Container> |
||||
|
</> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Profile; |
||||
|
|
@ -0,0 +1,89 @@ |
|||||
|
import React, { useState } from 'react'; |
||||
|
|
||||
|
import { Button, FormControl, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, useToast } from '@chakra-ui/react'; |
||||
|
import { SubmitHandler, useForm } from 'react-hook-form'; |
||||
|
|
||||
|
import { ItemCreate } from '../../client'; |
||||
|
import { useItemsStore } from '../../store/items-store'; |
||||
|
|
||||
|
interface CreateItemProps { |
||||
|
isOpen: boolean; |
||||
|
onClose: () => void; |
||||
|
} |
||||
|
|
||||
|
const CreateItem: React.FC<CreateItemProps> = ({ isOpen, onClose }) => { |
||||
|
const toast = useToast(); |
||||
|
const [isLoading, setIsLoading] = useState(false); |
||||
|
const { register, handleSubmit } = useForm<ItemCreate>(); |
||||
|
const { addItem } = useItemsStore(); |
||||
|
|
||||
|
const onSubmit: SubmitHandler<ItemCreate> = async (data) => { |
||||
|
try { |
||||
|
setIsLoading(true); |
||||
|
await addItem(data); |
||||
|
setIsLoading(false); |
||||
|
|
||||
|
toast({ |
||||
|
title: 'Success!', |
||||
|
description: 'Item created successfully.', |
||||
|
status: 'success', |
||||
|
isClosable: true, |
||||
|
}); |
||||
|
onClose(); |
||||
|
} catch (err) { |
||||
|
setIsLoading(false); |
||||
|
toast({ |
||||
|
title: 'Something went wrong.', |
||||
|
description: 'Failed to create item. Please try again.', |
||||
|
status: 'error', |
||||
|
isClosable: true, |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Modal |
||||
|
isOpen={isOpen} |
||||
|
onClose={onClose} |
||||
|
size={{ base: 'sm', md: 'md' }} |
||||
|
isCentered |
||||
|
> |
||||
|
<ModalOverlay /> |
||||
|
<ModalContent as="form" onSubmit={handleSubmit(onSubmit)}> |
||||
|
<ModalHeader>Create Item</ModalHeader> |
||||
|
<ModalCloseButton /> |
||||
|
<ModalBody pb={6}> |
||||
|
<FormControl> |
||||
|
<FormLabel>Title</FormLabel> |
||||
|
<Input |
||||
|
{...register('title')} |
||||
|
placeholder="Title" |
||||
|
|
||||
|
/> |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel>Description</FormLabel> |
||||
|
<Input |
||||
|
{...register('description')} |
||||
|
placeholder="Description" |
||||
|
|
||||
|
/> |
||||
|
</FormControl> |
||||
|
</ModalBody> |
||||
|
|
||||
|
<ModalFooter gap={3}> |
||||
|
<Button bg="ui.main" color="white" type="submit" isLoading={isLoading}> |
||||
|
Save |
||||
|
</Button> |
||||
|
<Button onClick={onClose} isDisabled={isLoading}> |
||||
|
Cancel |
||||
|
</Button> |
||||
|
</ModalFooter> |
||||
|
</ModalContent> |
||||
|
</Modal> |
||||
|
</> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default CreateItem; |
@ -0,0 +1,96 @@ |
|||||
|
import React, { useState } from 'react'; |
||||
|
|
||||
|
import { Box, Button, Checkbox, Flex, FormControl, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Spinner, useToast } from '@chakra-ui/react'; |
||||
|
import { SubmitHandler, useForm } from 'react-hook-form'; |
||||
|
|
||||
|
import { UserCreate } from '../../client'; |
||||
|
import { useUsersStore } from '../../store/users-store'; |
||||
|
|
||||
|
interface CreateUserProps { |
||||
|
isOpen: boolean; |
||||
|
onClose: () => void; |
||||
|
} |
||||
|
|
||||
|
const CreateUser: React.FC<CreateUserProps> = ({ isOpen, onClose }) => { |
||||
|
const toast = useToast(); |
||||
|
const [isLoading, setIsLoading] = useState(false); |
||||
|
const { register, handleSubmit } = useForm<UserCreate>(); |
||||
|
const { addUser } = useUsersStore(); |
||||
|
|
||||
|
const onSubmit: SubmitHandler<UserCreate> = async (data) => { |
||||
|
try { |
||||
|
setIsLoading(true); |
||||
|
await addUser(data); |
||||
|
setIsLoading(false); |
||||
|
toast({ |
||||
|
title: 'Success!', |
||||
|
description: 'User created successfully.', |
||||
|
status: 'success', |
||||
|
isClosable: true, |
||||
|
}); |
||||
|
onClose(); |
||||
|
|
||||
|
} catch (err) { |
||||
|
setIsLoading(false); |
||||
|
toast({ |
||||
|
title: 'Something went wrong.', |
||||
|
description: 'Failed to create user. Please try again.', |
||||
|
status: 'error', |
||||
|
isClosable: true, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Modal |
||||
|
isOpen={isOpen} |
||||
|
onClose={onClose} |
||||
|
size={{ base: "sm", md: "md" }} |
||||
|
isCentered |
||||
|
> |
||||
|
<ModalOverlay /> |
||||
|
<ModalContent as="form" onSubmit={handleSubmit(onSubmit)}> |
||||
|
{/* TODO: Check passwords */} |
||||
|
<ModalHeader>Create User</ModalHeader> |
||||
|
<ModalCloseButton /> |
||||
|
<ModalBody pb={6}> |
||||
|
<FormControl> |
||||
|
<FormLabel>Email</FormLabel> |
||||
|
<Input {...register('email')} placeholder='Email' type="email" /> |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel>Full name</FormLabel> |
||||
|
<Input {...register('full_name')} placeholder='Full name' type="text" /> |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel>Set Password</FormLabel> |
||||
|
<Input {...register('password')} placeholder='Password' type="password" /> |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel>Confirm Password</FormLabel> |
||||
|
<Input placeholder='Password' type="password" /> |
||||
|
</FormControl> |
||||
|
<Flex> |
||||
|
<FormControl mt={4}> |
||||
|
<Checkbox {...register('is_superuser')} colorScheme='teal'>Is superuser?</Checkbox> |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<Checkbox {...register('is_active')} colorScheme='teal'>Is active?</Checkbox> |
||||
|
</FormControl> |
||||
|
</Flex> |
||||
|
</ModalBody> |
||||
|
|
||||
|
<ModalFooter gap={3}> |
||||
|
<Button bg="ui.main" color="white" type="submit" isLoading={isLoading}> |
||||
|
Save |
||||
|
</Button> |
||||
|
<Button onClick={onClose}>Cancel</Button> |
||||
|
</ModalFooter> |
||||
|
</ModalContent> |
||||
|
</Modal> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default CreateUser; |
@ -0,0 +1,68 @@ |
|||||
|
import React, { useState } from 'react'; |
||||
|
|
||||
|
import { AlertDialog, AlertDialogBody, AlertDialogContent, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, Button } from '@chakra-ui/react'; |
||||
|
import { useForm } from 'react-hook-form'; |
||||
|
|
||||
|
import { useItemsStore } from '../../store/items-store'; |
||||
|
|
||||
|
interface DeleteProps { |
||||
|
toDelete: string; |
||||
|
id: number |
||||
|
isOpen: boolean; |
||||
|
onClose: () => void; |
||||
|
} |
||||
|
|
||||
|
const Delete: React.FC<DeleteProps> = ({ toDelete, id, isOpen, onClose }) => { |
||||
|
const cancelRef = React.useRef<HTMLButtonElement | null>(null); |
||||
|
const [isLoading, setIsLoading] = useState(false); |
||||
|
const { handleSubmit } = useForm(); |
||||
|
const { deleteItem } = useItemsStore(); |
||||
|
|
||||
|
const onSubmit = async () => { |
||||
|
try { |
||||
|
setIsLoading(true); |
||||
|
await deleteItem(id); |
||||
|
setIsLoading(false); |
||||
|
onClose(); |
||||
|
} catch (err) { |
||||
|
setIsLoading(false); |
||||
|
console.error(err); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<AlertDialog |
||||
|
isOpen={isOpen} |
||||
|
onClose={onClose} |
||||
|
leastDestructiveRef={cancelRef} |
||||
|
size={{ base: "sm", md: "md" }} |
||||
|
isCentered |
||||
|
> |
||||
|
<AlertDialogOverlay> |
||||
|
<AlertDialogContent as="form" onSubmit={handleSubmit(onSubmit)}> |
||||
|
<AlertDialogHeader fontSize='lg' fontWeight='bold'> |
||||
|
Delete {toDelete} |
||||
|
</AlertDialogHeader> |
||||
|
|
||||
|
<AlertDialogBody> |
||||
|
Are you sure? You will not be able to undo this action. |
||||
|
</AlertDialogBody> |
||||
|
|
||||
|
<AlertDialogFooter gap={3}> |
||||
|
<Button colorScheme='red' type="submit" isLoading={isLoading}> |
||||
|
Delete |
||||
|
</Button> |
||||
|
<Button ref={cancelRef} onClick={onClose} isDisabled={isLoading}> |
||||
|
Cancel |
||||
|
</Button> |
||||
|
</AlertDialogFooter> |
||||
|
</AlertDialogContent> |
||||
|
</AlertDialogOverlay> |
||||
|
</AlertDialog> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Delete; |
@ -0,0 +1,48 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Button, FormControl, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@chakra-ui/react'; |
||||
|
|
||||
|
interface EditItemProps { |
||||
|
isOpen: boolean; |
||||
|
onClose: () => void; |
||||
|
} |
||||
|
|
||||
|
const EditItem: React.FC<EditItemProps> = ({ isOpen, onClose }) => { |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Modal |
||||
|
isOpen={isOpen} |
||||
|
onClose={onClose} |
||||
|
size={{ base: "sm", md: "md" }} |
||||
|
isCentered |
||||
|
> |
||||
|
<ModalOverlay /> |
||||
|
<ModalContent> |
||||
|
<ModalHeader>Edit Item</ModalHeader> |
||||
|
<ModalCloseButton /> |
||||
|
<ModalBody pb={6}> |
||||
|
<FormControl> |
||||
|
<FormLabel>Item</FormLabel> |
||||
|
<Input placeholder='Item' /> |
||||
|
</FormControl> |
||||
|
|
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel>Description</FormLabel> |
||||
|
<Input placeholder='Description' /> |
||||
|
</FormControl> |
||||
|
</ModalBody> |
||||
|
|
||||
|
<ModalFooter gap={3}> |
||||
|
<Button colorScheme='teal'> |
||||
|
Save |
||||
|
</Button> |
||||
|
<Button onClick={onClose}>Cancel</Button> |
||||
|
</ModalFooter> |
||||
|
</ModalContent> |
||||
|
</Modal> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default EditItem; |
@ -0,0 +1,60 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Button, Checkbox, Flex, FormControl, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, useDisclosure } from '@chakra-ui/react'; |
||||
|
|
||||
|
interface EditUserProps { |
||||
|
isOpen: boolean; |
||||
|
onClose: () => void; |
||||
|
} |
||||
|
|
||||
|
const EditUser: React.FC<EditUserProps> = ({ isOpen, onClose }) => { |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Modal |
||||
|
isOpen={isOpen} |
||||
|
onClose={onClose} |
||||
|
size={{ base: "sm", md: "md" }} |
||||
|
isCentered |
||||
|
> |
||||
|
<ModalOverlay /> |
||||
|
<ModalContent> |
||||
|
<ModalHeader>Edit User</ModalHeader> |
||||
|
<ModalCloseButton /> |
||||
|
<ModalBody pb={6}> |
||||
|
<FormControl> |
||||
|
<FormLabel>Email</FormLabel> |
||||
|
<Input placeholder='Email' /> |
||||
|
</FormControl> |
||||
|
|
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel>Full name</FormLabel> |
||||
|
<Input placeholder='Full name' /> |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel>Set Password</FormLabel> |
||||
|
<Input placeholder='Password' type="password" /> |
||||
|
</FormControl> |
||||
|
<Flex> |
||||
|
<FormControl mt={4}> |
||||
|
<Checkbox colorScheme='teal'>Is superuser?</Checkbox> |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<Checkbox colorScheme='teal'>Is active?</Checkbox> |
||||
|
</FormControl> |
||||
|
</Flex> |
||||
|
</ModalBody> |
||||
|
|
||||
|
<ModalFooter gap={3}> |
||||
|
<Button colorScheme='teal'> |
||||
|
Save |
||||
|
</Button> |
||||
|
<Button onClick={onClose}>Cancel</Button> |
||||
|
</ModalFooter> |
||||
|
</ModalContent> |
||||
|
</Modal> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default EditUser; |
@ -0,0 +1,32 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Button, Container, Heading, Radio, RadioGroup, Stack } from '@chakra-ui/react'; |
||||
|
|
||||
|
const Appearance: React.FC = () => { |
||||
|
const [value, setValue] = React.useState('system'); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Container maxW="full"> |
||||
|
<Heading size="sm" py={4}> |
||||
|
Appearance |
||||
|
</Heading> |
||||
|
<RadioGroup onChange={setValue} value={value}> |
||||
|
<Stack> |
||||
|
<Radio value="system" colorScheme="teal" defaultChecked> |
||||
|
Use system settings (default) |
||||
|
</Radio> |
||||
|
<Radio value="light" colorScheme="teal"> |
||||
|
Light |
||||
|
</Radio> |
||||
|
<Radio value="dark" colorScheme="teal"> |
||||
|
Dark |
||||
|
</Radio> |
||||
|
</Stack> |
||||
|
</RadioGroup> |
||||
|
<Button colorScheme='teal' mt={4}>Save</Button> |
||||
|
</ Container> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
export default Appearance; |
@ -0,0 +1,34 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Box, Button, Container, FormControl, FormLabel, Heading, Input } from '@chakra-ui/react'; |
||||
|
|
||||
|
const ChangePassword: React.FC = () => { |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Container maxW="full"> |
||||
|
<Heading size="sm" py={4}> |
||||
|
Change Password |
||||
|
</Heading> |
||||
|
<Box as="form" display="flex" flexDirection="column" alignItems="start"> |
||||
|
<FormControl> |
||||
|
<FormLabel color="gray.700">Old password</FormLabel> |
||||
|
<Input placeholder='Password' type="password" /> |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel color="gray.700">New password</FormLabel> |
||||
|
<Input placeholder='Password' type="password" /> |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel color="gray.700">Confirm new password</FormLabel> |
||||
|
<Input placeholder='Password' type="password" /> |
||||
|
</FormControl> |
||||
|
<Button colorScheme='teal' mt={4} type="submit"> |
||||
|
Save |
||||
|
</Button> |
||||
|
</Box> |
||||
|
</ Container> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
export default ChangePassword; |
@ -0,0 +1,23 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Button, Container, Heading, Text } from '@chakra-ui/react'; |
||||
|
|
||||
|
const DeleteAccount: React.FC = () => { |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Container maxW="full"> |
||||
|
<Heading size="sm" py={4}> |
||||
|
Delete Account |
||||
|
</Heading> |
||||
|
<Text> |
||||
|
Are you sure you want to delete your account? This action cannot be undone. |
||||
|
</Text> |
||||
|
<Button colorScheme='red' mt={4}> |
||||
|
Delete |
||||
|
</Button> |
||||
|
</ Container> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
export default DeleteAccount; |
@ -0,0 +1,49 @@ |
|||||
|
import React, { useState } from 'react'; |
||||
|
|
||||
|
import { Button, Container, FormControl, FormLabel, Heading, Input, Text } from '@chakra-ui/react'; |
||||
|
|
||||
|
import { useUserStore } from '../../store/user-store'; |
||||
|
|
||||
|
const UserInformation: React.FC = () => { |
||||
|
const [editMode, setEditMode] = useState(false); |
||||
|
const { user } = useUserStore(); |
||||
|
|
||||
|
|
||||
|
const toggleEditMode = () => { |
||||
|
setEditMode(!editMode); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Container maxW="full"> |
||||
|
<Heading size="sm" py={4}> |
||||
|
User Information |
||||
|
</Heading> |
||||
|
<FormControl> |
||||
|
<FormLabel color="gray.700">Full name</FormLabel> |
||||
|
{ |
||||
|
editMode ? |
||||
|
<Input placeholder={user?.full_name || "Full name"} type="text" /> : |
||||
|
<Text> |
||||
|
{user?.full_name || "N/A"} |
||||
|
</Text> |
||||
|
} |
||||
|
</FormControl> |
||||
|
<FormControl mt={4}> |
||||
|
<FormLabel color="gray.700">Email</FormLabel> |
||||
|
{ |
||||
|
editMode ? |
||||
|
<Input placeholder={user?.email} type="text" /> : |
||||
|
<Text> |
||||
|
{user?.email || "N/A"} |
||||
|
</Text> |
||||
|
} |
||||
|
</FormControl> |
||||
|
<Button colorScheme='teal' mt={4} onClick={toggleEditMode}> |
||||
|
{editMode ? "Save" : "Edit"} |
||||
|
</Button> |
||||
|
</ Container> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
export default UserInformation; |
Loading…
Reference in new issue