2023-08-31 08:17:52 -04:00
|
|
|
import { Popover, Transition } from '@headlessui/react'
|
2023-07-27 18:59:40 -04:00
|
|
|
import { ActionButton } from './ActionButton'
|
2024-04-16 14:29:33 -04:00
|
|
|
import { faBug, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'
|
2023-08-15 22:33:52 -04:00
|
|
|
import { faGithub } from '@fortawesome/free-brands-svg-icons'
|
2023-10-06 10:00:35 -04:00
|
|
|
import { useLocation, useNavigate } from 'react-router-dom'
|
2023-08-31 08:17:52 -04:00
|
|
|
import { Fragment, useState } from 'react'
|
2024-02-11 12:59:00 +11:00
|
|
|
import { paths } from 'lib/paths'
|
2023-08-22 05:34:20 +10:00
|
|
|
import { Models } from '@kittycad/lib'
|
2024-03-11 20:26:13 -04:00
|
|
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
2023-10-04 18:00:55 -04:00
|
|
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
2024-04-16 14:29:33 -04:00
|
|
|
import Tooltip from './Tooltip'
|
2023-08-22 05:34:20 +10:00
|
|
|
|
|
|
|
type User = Models['User_type']
|
2023-07-27 18:59:40 -04:00
|
|
|
|
|
|
|
const UserSidebarMenu = ({ user }: { user?: User }) => {
|
2023-10-06 10:00:35 -04:00
|
|
|
const location = useLocation()
|
2023-10-04 18:00:55 -04:00
|
|
|
const filePath = useAbsoluteFilePath()
|
2023-08-01 13:23:17 -04:00
|
|
|
const displayedName = getDisplayName(user)
|
|
|
|
const [imageLoadFailed, setImageLoadFailed] = useState(false)
|
2023-07-27 18:59:40 -04:00
|
|
|
const navigate = useNavigate()
|
2024-03-11 20:26:13 -04:00
|
|
|
const send = useSettingsAuthContext()?.auth?.send
|
2023-07-27 18:59:40 -04:00
|
|
|
|
2023-08-01 13:23:17 -04:00
|
|
|
// Fallback logic for displaying user's "name":
|
|
|
|
// 1. user.name
|
|
|
|
// 2. user.first_name + ' ' + user.last_name
|
|
|
|
// 3. user.first_name
|
|
|
|
// 4. user.email
|
|
|
|
function getDisplayName(user?: User) {
|
|
|
|
if (!user) return null
|
|
|
|
if (user.name) return user.name
|
|
|
|
if (user.first_name) {
|
|
|
|
if (user.last_name) return user.first_name + ' ' + user.last_name
|
|
|
|
return user.first_name
|
|
|
|
}
|
|
|
|
return user.email
|
|
|
|
}
|
|
|
|
|
2023-07-27 18:59:40 -04:00
|
|
|
return (
|
|
|
|
<Popover className="relative">
|
2023-08-01 13:23:17 -04:00
|
|
|
{user?.image && !imageLoadFailed ? (
|
|
|
|
<Popover.Button
|
2024-06-20 14:06:11 -04:00
|
|
|
className="relative border-0 rounded-full w-fit min-w-max p-0 group"
|
2023-08-01 13:23:17 -04:00
|
|
|
data-testid="user-sidebar-toggle"
|
|
|
|
>
|
2023-12-06 14:44:13 -05:00
|
|
|
<div className="rounded-full border overflow-hidden">
|
2023-07-27 18:59:40 -04:00
|
|
|
<img
|
|
|
|
src={user?.image || ''}
|
|
|
|
alt={user?.name || ''}
|
2023-12-06 14:44:13 -05:00
|
|
|
className="h-8 w-8 rounded-full"
|
2023-07-27 18:59:40 -04:00
|
|
|
referrerPolicy="no-referrer"
|
2023-08-01 13:23:17 -04:00
|
|
|
onError={() => setImageLoadFailed(true)}
|
2023-07-27 18:59:40 -04:00
|
|
|
/>
|
|
|
|
</div>
|
2024-06-20 14:06:11 -04:00
|
|
|
<Tooltip position="bottom-right" delay={1000}>
|
|
|
|
User menu
|
|
|
|
</Tooltip>
|
2023-07-27 18:59:40 -04:00
|
|
|
</Popover.Button>
|
|
|
|
) : (
|
|
|
|
<ActionButton
|
|
|
|
Element={Popover.Button}
|
2024-05-10 19:02:11 -04:00
|
|
|
iconStart={{ icon: 'menu' }}
|
2024-04-16 14:29:33 -04:00
|
|
|
className="border-transparent !px-0"
|
2023-08-01 13:23:17 -04:00
|
|
|
data-testid="user-sidebar-toggle"
|
2023-07-27 18:59:40 -04:00
|
|
|
>
|
2024-06-20 14:06:11 -04:00
|
|
|
<Tooltip position="bottom-right" delay={1000}>
|
2024-04-16 14:29:33 -04:00
|
|
|
User menu
|
|
|
|
</Tooltip>
|
2023-07-27 18:59:40 -04:00
|
|
|
</ActionButton>
|
|
|
|
)}
|
2023-08-31 08:17:52 -04:00
|
|
|
<Transition
|
|
|
|
enter="duration-200 ease-out"
|
|
|
|
enterFrom="opacity-0"
|
|
|
|
enterTo="opacity-100"
|
|
|
|
leave="duration-100 ease-in"
|
|
|
|
leaveFrom="opacity-100"
|
|
|
|
leaveTo="opacity-0"
|
|
|
|
as={Fragment}
|
|
|
|
>
|
|
|
|
<Popover.Overlay className="fixed z-20 inset-0 bg-chalkboard-110/50" />
|
|
|
|
</Transition>
|
2023-07-27 18:59:40 -04:00
|
|
|
|
2023-08-31 08:17:52 -04:00
|
|
|
<Transition
|
|
|
|
enter="duration-100 ease-out"
|
|
|
|
enterFrom="opacity-0 translate-x-1/4"
|
|
|
|
enterTo="opacity-100 translate-x-0"
|
|
|
|
leave="duration-75 ease-in"
|
|
|
|
leaveFrom="opacity-100 translate-x-0"
|
|
|
|
leaveTo="opacity-0 translate-x-4"
|
|
|
|
as={Fragment}
|
|
|
|
>
|
2023-12-06 14:44:13 -05:00
|
|
|
<Popover.Panel className="fixed inset-0 left-auto z-30 w-64 bg-chalkboard-10 dark:bg-chalkboard-90 border border-chalkboard-30 dark:border-chalkboard-80 shadow-md rounded-l-md overflow-hidden">
|
2023-08-31 08:17:52 -04:00
|
|
|
{({ close }) => (
|
|
|
|
<>
|
|
|
|
{user && (
|
2023-12-06 14:44:13 -05:00
|
|
|
<div className="flex items-center gap-4 px-4 py-3 bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-b border-b-chalkboard-30 dark:border-b-chalkboard-80">
|
2023-08-31 08:17:52 -04:00
|
|
|
{user.image && !imageLoadFailed && (
|
|
|
|
<div className="rounded-full shadow-inner overflow-hidden">
|
|
|
|
<img
|
|
|
|
src={user.image}
|
|
|
|
alt={user.name || ''}
|
|
|
|
className="h-8 w-8"
|
|
|
|
referrerPolicy="no-referrer"
|
|
|
|
onError={() => setImageLoadFailed(true)}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
)}
|
2023-08-10 13:30:32 -04:00
|
|
|
|
2023-08-31 08:17:52 -04:00
|
|
|
<div>
|
2023-12-06 14:44:13 -05:00
|
|
|
<p className="m-0 text-mono" data-testid="username">
|
2023-08-31 08:17:52 -04:00
|
|
|
{displayedName || ''}
|
2023-08-10 13:30:32 -04:00
|
|
|
</p>
|
2023-08-31 08:17:52 -04:00
|
|
|
{displayedName !== user.email && (
|
|
|
|
<p
|
2023-12-06 14:44:13 -05:00
|
|
|
className="m-0 text-chalkboard-70 dark:text-chalkboard-40 text-xs"
|
2023-08-31 08:17:52 -04:00
|
|
|
data-testid="email"
|
|
|
|
>
|
|
|
|
{user.email}
|
|
|
|
</p>
|
|
|
|
)}
|
|
|
|
</div>
|
2023-08-10 13:30:32 -04:00
|
|
|
</div>
|
2023-08-31 08:17:52 -04:00
|
|
|
)}
|
|
|
|
<div className="p-4 flex flex-col gap-2">
|
|
|
|
<ActionButton
|
|
|
|
Element="button"
|
2024-05-10 19:02:11 -04:00
|
|
|
iconStart={{ icon: 'settings' }}
|
2023-12-06 14:44:13 -05:00
|
|
|
className="border-transparent dark:border-transparent hover:bg-transparent"
|
2023-08-31 08:17:52 -04:00
|
|
|
onClick={() => {
|
|
|
|
// since /settings is a nested route the sidebar doesn't close
|
|
|
|
// automatically when navigating to it
|
|
|
|
close()
|
2023-10-06 10:00:35 -04:00
|
|
|
const targetPath = location.pathname.includes(paths.FILE)
|
|
|
|
? filePath + paths.SETTINGS
|
|
|
|
: paths.HOME + paths.SETTINGS
|
|
|
|
navigate(targetPath)
|
2023-08-31 08:17:52 -04:00
|
|
|
}}
|
2024-01-04 04:54:07 -05:00
|
|
|
data-testid="settings-button"
|
2023-08-31 08:17:52 -04:00
|
|
|
>
|
|
|
|
Settings
|
|
|
|
</ActionButton>
|
|
|
|
<ActionButton
|
2023-10-02 13:26:23 -04:00
|
|
|
Element="externalLink"
|
2023-08-31 08:17:52 -04:00
|
|
|
to="https://github.com/KittyCAD/modeling-app/discussions"
|
2024-05-10 19:02:11 -04:00
|
|
|
iconStart={{ icon: faGithub, className: 'p-1', size: 'sm' }}
|
2023-12-06 14:44:13 -05:00
|
|
|
className="border-transparent dark:border-transparent"
|
2023-08-31 08:17:52 -04:00
|
|
|
>
|
|
|
|
Request a feature
|
|
|
|
</ActionButton>
|
2023-10-02 16:33:33 -07:00
|
|
|
<ActionButton
|
|
|
|
Element="externalLink"
|
2024-02-27 15:10:50 -07:00
|
|
|
to="https://github.com/KittyCAD/modeling-app/issues/new/choose"
|
2024-05-10 19:02:11 -04:00
|
|
|
iconStart={{ icon: faBug, className: 'p-1', size: 'sm' }}
|
2023-12-06 14:44:13 -05:00
|
|
|
className="border-transparent dark:border-transparent"
|
2023-10-02 16:33:33 -07:00
|
|
|
>
|
|
|
|
Report a bug
|
|
|
|
</ActionButton>
|
2023-08-31 08:17:52 -04:00
|
|
|
<ActionButton
|
|
|
|
Element="button"
|
|
|
|
onClick={() => send('Log out')}
|
2024-05-10 19:02:11 -04:00
|
|
|
iconStart={{
|
2023-08-31 08:17:52 -04:00
|
|
|
icon: faSignOutAlt,
|
2023-12-06 14:44:13 -05:00
|
|
|
className: 'p-1',
|
2024-04-05 00:59:02 -04:00
|
|
|
bgClassName: '!bg-transparent',
|
2023-12-06 14:44:13 -05:00
|
|
|
size: 'sm',
|
2024-04-05 00:59:02 -04:00
|
|
|
iconClassName: '!text-destroy-70',
|
2023-08-31 08:17:52 -04:00
|
|
|
}}
|
2023-12-06 14:44:13 -05:00
|
|
|
className="border-transparent dark:border-transparent hover:border-destroy-40 dark:hover:border-destroy-60 hover:bg-destroy-10/20 dark:hover:bg-destroy-80/20"
|
2023-11-29 05:15:04 -05:00
|
|
|
data-testid="user-sidebar-sign-out"
|
2023-08-31 08:17:52 -04:00
|
|
|
>
|
|
|
|
Sign out
|
|
|
|
</ActionButton>
|
2023-08-01 13:23:17 -04:00
|
|
|
</div>
|
2023-08-31 08:17:52 -04:00
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</Popover.Panel>
|
|
|
|
</Transition>
|
2023-07-27 18:59:40 -04:00
|
|
|
</Popover>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default UserSidebarMenu
|