JFIFxxC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr{ gilour
import clsx from 'clsx'; import {cloneElement, Fragment, ReactElement, useEffect, useState} from 'react'; import {ButtonBase} from '../ui/buttons/button-base'; import {PersonalWorkspace, useUserWorkspaces} from './user-workspaces'; import {UnfoldMoreIcon} from '../icons/material/UnfoldMore'; import {AddIcon} from '../icons/material/Add'; import {NewWorkspaceDialog} from './new-workspace-dialog'; import {WorkspaceMembersDialog} from './workspace-members-dialog'; import {useActiveWorkspaceId} from './active-workspace-id-context'; import {DialogTrigger} from '../ui/overlays/dialog/dialog-trigger'; import {Workspace} from './types/workspace'; import {Dialog} from '../ui/overlays/dialog/dialog'; import {DialogBody} from '../ui/overlays/dialog/dialog-body'; import {Button, ButtonProps} from '../ui/buttons/button'; import {CheckIcon} from '../icons/material/Check'; import {Menu, MenuItem, MenuTrigger} from '../ui/navigation/menu/menu-trigger'; import {KeyboardArrowDownIcon} from '../icons/material/KeyboardArrowDown'; import {PersonAddIcon} from '../icons/material/PersonAdd'; import {DeleteIcon} from '../icons/material/Delete'; import {ExitToAppIcon} from '../icons/material/ExitToApp'; import {EditIcon} from '../icons/material/Edit'; import {RenameWorkspaceDialog} from './rename-workspace-dialog'; import {ConfirmationDialog} from '../ui/overlays/dialog/confirmation-dialog'; import {useDeleteWorkspace} from './requests/delete-workspace'; import {useRemoveMember} from './requests/remove-member'; import {useAuth} from '../auth/use-auth'; import {Trans} from '../i18n/trans'; import {LeaveWorkspaceConfirmation} from './leave-workspace-confirmation'; import {openDialog} from '@common/ui/overlays/store/dialog-store'; import {useDialogContext} from '@common/ui/overlays/dialog/dialog-context'; interface WorkspaceSelectorProps { className?: string; onChange?: (id: number) => void; trigger?: ReactElement<ButtonProps>; } export function WorkspaceSelector({ onChange, className, trigger: propsTrigger, }: WorkspaceSelectorProps) { const {data: workspaces, isFetched, isFetching} = useUserWorkspaces(); const {workspaceId, setWorkspaceId} = useActiveWorkspaceId(); const activeWorkspace = workspaces?.find(w => w.id === workspaceId); const [selectorIsOpen, setSelectorIsOpen] = useState(false); const {hasPermission} = useAuth(); // if user no longer has access to previously selected workspace, select personal one useEffect(() => { // make sure we don't unset active workspace while user workspaces are being re-fetched if (isFetched && !isFetching && !activeWorkspace) { setWorkspaceId(PersonalWorkspace.id); } }, [activeWorkspace, workspaces, setWorkspaceId, isFetched, isFetching]); if ( !activeWorkspace || (!hasPermission('workspaces.create') && workspaces?.length === 1) ) { return null; } const defaultTrigger = ( <ButtonBase className={clsx( 'flex items-center gap-10 rounded ring-inset hover:bg-hover focus-visible:ring-2', className, )} > <span className="mr-auto block flex-auto overflow-hidden text-left"> <span className="block overflow-hidden overflow-ellipsis text-sm font-medium text-main"> {activeWorkspace.default ? ( <Trans message={activeWorkspace.name} /> ) : ( activeWorkspace.name )} </span> <span className="block text-xs text-muted"> {activeWorkspace.default ? ( <Trans message="Personal workspace" /> ) : ( <Trans message=":count members" values={{count: activeWorkspace.members_count}} /> )} </span> </span> <UnfoldMoreIcon className="shrink-0 icon-md" /> </ButtonBase> ); const trigger = propsTrigger || defaultTrigger; return ( <Fragment> <DialogTrigger type="popover" isOpen={selectorIsOpen} onClose={() => { setSelectorIsOpen(false); }} > {cloneElement(trigger, { onClick: () => setSelectorIsOpen(!selectorIsOpen), })} <Dialog size="min-w-320"> <DialogBody padding="p-10"> <div className="mb-16 border-b pb-10"> {workspaces?.map(workspace => ( <WorkspaceItem key={workspace.id} workspace={workspace} setSelectorIsOpen={setSelectorIsOpen} onChange={onChange} /> ))} </div> <div className="mb-4 px-4 text-center"> <Button onClick={async e => { e.preventDefault(); e.stopPropagation(); setSelectorIsOpen(false); const workspaceId = await openDialog(NewWorkspaceDialog); if (workspaceId) { setWorkspaceId(workspaceId); onChange?.(workspaceId); } }} variant="outline" startIcon={<AddIcon />} color="primary" className="h-40 w-full" > <Trans message="Create new workspace" /> </Button> </div> </DialogBody> </Dialog> </DialogTrigger> </Fragment> ); } interface WorkspaceItemProps { workspace: Workspace; onChange: WorkspaceSelectorProps['onChange']; setSelectorIsOpen: (value: boolean) => void; } function WorkspaceItem({ workspace, onChange, setSelectorIsOpen, }: WorkspaceItemProps) { const {workspaceId, setWorkspaceId} = useActiveWorkspaceId(); const isActive = workspaceId === workspace.id; return ( <div onClick={() => { setWorkspaceId(workspace.id); onChange?.(workspace.id); setSelectorIsOpen(false); }} className={clsx( 'mb-4 flex cursor-pointer items-center gap-12 rounded-lg p-10 text-left', isActive && 'bg-primary/5', !isActive && 'hover:bg-hover', )} > <CheckIcon size="sm" className={clsx('flex-shrink-0 text-primary', !isActive && 'invisible')} /> <div className="flex-auto"> <div className={clsx('text-sm', isActive && 'font-semibold')}> {workspace.name} </div> <div className="text-sm text-muted"> {workspace.default ? ( <Trans message="Personal workspace" /> ) : ( <Trans message=":count members" values={{count: workspace.members_count}} /> )} </div> </div> {workspace.id !== 0 && ( <ManageButton setSelectorIsOpen={setSelectorIsOpen} workspace={workspace} onChange={onChange} /> )} </div> ); } interface LeaveWorkspaceDialogProps { workspace: Workspace; onChange?: (id: number) => void; } function LeaveWorkspaceDialog({ workspace, onChange, }: LeaveWorkspaceDialogProps) { const removeMember = useRemoveMember(); const {user} = useAuth(); const {close} = useDialogContext(); return ( <LeaveWorkspaceConfirmation isLoading={removeMember.isPending} onConfirm={() => { removeMember.mutate( { workspaceId: workspace.id, memberId: user!.id, memberType: 'member', }, { onSuccess: () => { close(); onChange?.(PersonalWorkspace.id); }, }, ); }} /> ); } interface DeleteWorkspaceConfirmationProps { workspace: Workspace; onChange?: (id: number) => void; } function DeleteWorkspaceConfirmation({ workspace, onChange, }: DeleteWorkspaceConfirmationProps) { const deleteWorkspace = useDeleteWorkspace(); const {close} = useDialogContext(); return ( <ConfirmationDialog isDanger title={<Trans message="Delete workspace" />} body={ <Trans message="Are you sure you want to delete “:name“?" values={{name: workspace.name}} /> } confirm={<Trans message="Delete" />} isLoading={deleteWorkspace.isPending} onConfirm={() => { deleteWorkspace.mutate( {id: workspace.id}, { onSuccess: () => { close(); onChange?.(PersonalWorkspace.id); }, }, ); }} /> ); } interface ManageButtonProps { setSelectorIsOpen: (value: boolean) => void; workspace: Workspace; onChange?: (id: number) => void; } function ManageButton({ setSelectorIsOpen, workspace, onChange, }: ManageButtonProps) { const {user} = useAuth(); return ( <MenuTrigger onItemSelected={() => setSelectorIsOpen(false)}> <Button onClick={e => { e.preventDefault(); e.stopPropagation(); }} color="primary" size="xs" variant="outline" endIcon={<KeyboardArrowDownIcon />} > <Trans message="Manage" /> </Button> <Menu> <MenuItem onClick={e => { e.stopPropagation(); openDialog(WorkspaceMembersDialog, {workspace}); }} value="workspaceMembers" startIcon={<PersonAddIcon />} > <Trans message="Members" /> </MenuItem> {workspace.owner_id === user?.id && ( <MenuItem onClick={e => { e.stopPropagation(); openDialog(RenameWorkspaceDialog, {workspace}); }} value="updateWorkspace" startIcon={<EditIcon />} > <Trans message="Rename" /> </MenuItem> )} {workspace.owner_id !== user?.id && ( <MenuItem onClick={e => { e.stopPropagation(); openDialog(LeaveWorkspaceDialog, {workspace, onChange}); }} value="leaveWorkspace" startIcon={<ExitToAppIcon />} > <Trans message="Leave" /> </MenuItem> )} {workspace.owner_id === user?.id && ( <MenuItem onClick={e => { e.stopPropagation(); openDialog(DeleteWorkspaceConfirmation, {workspace, onChange}); }} value="deleteWorkspace" startIcon={<DeleteIcon />} > <Trans message="Delete" /> </MenuItem> )} </Menu> </MenuTrigger> ); }