This commit is contained in:
2024-04-21 14:42:52 +02:00
parent 4b69674ede
commit 8a25f53c99
10700 changed files with 55767 additions and 14201 deletions

View File

@ -0,0 +1,71 @@
import { useRef } from 'react'
import {
Divider,
Drawer as ChakraDrawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerHeader,
DrawerOverlay,
IconButton,
useDisclosure,
Circle,
} from '@chakra-ui/react'
import cx from 'classnames'
import NotificationAPI from '@/client/api/notification'
import { swrConfig } from '@/client/options'
import { IconNotifications } from '@/lib'
import NotificationDrawerItem from './notification-drawer-item'
const TopBarNotificationDrawer = () => {
const buttonRef = useRef<HTMLButtonElement>(null)
const { isOpen, onOpen, onClose } = useDisclosure()
const { data: notfications } = NotificationAPI.useGetAll(swrConfig())
return (
<>
<div className={cx('flex', 'items-center', 'justify-center', 'relative')}>
<IconButton
ref={buttonRef}
icon={<IconNotifications />}
aria-label=""
onClick={onOpen}
/>
{notfications && notfications.length > 0 && (
<Circle size="15px" bg="red" position="absolute" top={0} right={0} />
)}
</div>
<ChakraDrawer
isOpen={isOpen}
placement="right"
onClose={onClose}
finalFocusRef={buttonRef}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>Notifications</DrawerHeader>
<DrawerBody>
{notfications && notfications.length > 0 ? (
<div className={cx('flex', 'flex-col', 'gap-1.5')}>
{notfications.map((n, index) => (
<div
key={index}
className={cx('flex', 'flex-col', 'gap-1.5')}
>
<NotificationDrawerItem notification={n} />
{index !== notfications.length - 1 && <Divider />}
</div>
))}
</div>
) : (
<span>There are no notifications.</span>
)}
</DrawerBody>
</DrawerContent>
</ChakraDrawer>
</>
)
}
export default TopBarNotificationDrawer

View File

@ -0,0 +1,19 @@
import { Invitation } from '@/client/api/invitation'
import { Notification } from '@/client/api/notification'
import NotificationDrawerNewInvitationItem from './notification-drawer-new-Invitation-item'
export type NotificationDrawerItemProps = {
notification: Notification
}
const NotificationDrawerItem = ({
notification,
}: NotificationDrawerItemProps) => {
if (notification.type === 'new_invitation') {
const body: Invitation = notification.body as Invitation
return <NotificationDrawerNewInvitationItem invitation={body} />
}
return null
}
export default NotificationDrawerItem

View File

@ -0,0 +1,96 @@
import { useCallback, useState } from 'react'
import { Button, useToast } from '@chakra-ui/react'
import { useSWRConfig } from 'swr'
import cx from 'classnames'
import InvitationAPI, { Invitation } from '@/client/api/invitation'
import userToString from '@/helpers/user-to-string'
export type NewInvitationProps = {
invitation: Invitation
}
const NotificationDrawerNewInvitationItem = ({
invitation,
}: NewInvitationProps) => {
const { mutate } = useSWRConfig()
const toast = useToast()
const [isAcceptLoading, setIsAcceptLoading] = useState(false)
const [isDeclineLoading, setIsDeclineLoading] = useState(false)
const handleAccept = useCallback(
async (invitationId: string) => {
try {
setIsAcceptLoading(true)
await InvitationAPI.accept(invitationId)
mutate('/notifications')
mutate('/invitations/get_incoming')
mutate('/organizations')
toast({
title: 'Invitation accepted',
status: 'success',
isClosable: true,
})
} finally {
setIsAcceptLoading(false)
}
},
[mutate, toast],
)
const handleDecline = useCallback(
async (invitationId: string) => {
try {
setIsDeclineLoading(true)
await InvitationAPI.decline(invitationId)
mutate('/notifications')
mutate('/invitations/get_incoming')
toast({
title: 'Invitation declined',
status: 'info',
isClosable: true,
})
} finally {
setIsDeclineLoading(false)
}
},
[mutate, toast],
)
return (
<div className={cx('flex', 'flex-col', 'gap-0.5')}>
<div>
You have been invited by{' '}
<span className={cx('font-bold')}>
{userToString(invitation.owner)}
</span>{' '}
to join the organization{' '}
<span className={cx('font-bold')}>{invitation.organization.name}</span>
.<br />
</div>
<div className={cx('flex', 'flex-row', 'gap-0.5', 'justify-end')}>
<Button
size="sm"
variant="ghost"
colorScheme="blue"
disabled={isAcceptLoading || isDeclineLoading}
isLoading={isAcceptLoading}
onClick={() => handleAccept(invitation.id)}
>
Accept
</Button>
<Button
size="sm"
variant="ghost"
colorScheme="red"
disabled={isDeclineLoading || isAcceptLoading}
isLoading={isDeclineLoading}
onClick={() => handleDecline(invitation.id)}
>
Decline
</Button>
</div>
</div>
)
}
export default NotificationDrawerNewInvitationItem