fix(admin): adjust admin panel style (#11065)

close AF-2353 AF-2354 AF-2355 AF-2356 AF-2358 AF-2360 AF-2362 AF-2363 AF-2364 AF-2389
Fixed some UI issues.
This commit is contained in:
JimmFly
2025-03-26 03:16:14 +00:00
parent ce7e3330f4
commit 28854ef274
16 changed files with 180 additions and 139 deletions
@@ -5,16 +5,10 @@ import {
} from '@affine/admin/components/ui/avatar'; } from '@affine/admin/components/ui/avatar';
import type { UserType } from '@affine/graphql'; import type { UserType } from '@affine/graphql';
import { FeatureType } from '@affine/graphql'; import { FeatureType } from '@affine/graphql';
import { AccountIcon, LockIcon, UnlockIcon } from '@blocksuite/icons/rc';
import type { ColumnDef } from '@tanstack/react-table'; import type { ColumnDef } from '@tanstack/react-table';
import { cssVarV2 } from '@toeverything/theme/v2'; import { cssVarV2 } from '@toeverything/theme/v2';
import clsx from 'clsx'; import { MailIcon } from 'lucide-react';
import {
LockIcon,
MailIcon,
MailWarningIcon,
UnlockIcon,
UserIcon,
} from 'lucide-react';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { Checkbox } from '../../../components/ui/checkbox'; import { Checkbox } from '../../../components/ui/checkbox';
@@ -35,10 +29,10 @@ const StatusItem = ({
textFalse: string; textFalse: string;
}) => ( }) => (
<div <div
className={clsx( className="flex gap-1 items-center"
'flex gap-2 items-center', style={{
!condition ? 'text-red-500 opacity-100' : 'opacity-25' color: condition ? cssVarV2('text/secondary') : cssVarV2('status/error'),
)} }}
> >
{condition ? ( {condition ? (
<> <>
@@ -89,7 +83,7 @@ export const columns: ColumnDef<UserType>[] = [
<Avatar className="w-10 h-10"> <Avatar className="w-10 h-10">
<AvatarImage src={row.original.avatarUrl ?? undefined} /> <AvatarImage src={row.original.avatarUrl ?? undefined} />
<AvatarFallback> <AvatarFallback>
<UserIcon size={20} /> <AccountIcon fontSize={20} />
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<div className="flex flex-col gap-1 max-w-full overflow-hidden"> <div className="flex flex-col gap-1 max-w-full overflow-hidden">
@@ -97,7 +91,7 @@ export const columns: ColumnDef<UserType>[] = [
<span>{row.original.name}</span> <span>{row.original.name}</span>
{row.original.features.includes(FeatureType.Admin) && ( {row.original.features.includes(FeatureType.Admin) && (
<span <span
className="ml-2 rounded px-2 py-0.5 text-xs h-5 border" className="ml-2 rounded px-2 py-0.5 text-xs h-5 border text-center inline-flex items-center font-normal"
style={{ style={{
borderRadius: '4px', borderRadius: '4px',
backgroundColor: cssVarV2('chip/label/blue'), backgroundColor: cssVarV2('chip/label/blue'),
@@ -120,7 +114,12 @@ export const columns: ColumnDef<UserType>[] = [
</span> </span>
)} )}
</div> </div>
<div className="text-xs font-medium opacity-50 max-w-full overflow-hidden"> <div
className="text-xs font-medium max-w-full overflow-hidden"
style={{
color: cssVarV2('text/secondary'),
}}
>
{row.original.email} {row.original.email}
</div> </div>
</div> </div>
@@ -141,20 +140,29 @@ export const columns: ColumnDef<UserType>[] = [
cell: ({ row: { original: user } }) => ( cell: ({ row: { original: user } }) => (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="flex flex-col gap-2 text-xs max-md:hidden"> <div className="flex flex-col gap-2 text-xs max-md:hidden">
<div className="flex justify-end opacity-25">{user.id}</div> <div className="flex justify-start">{user.id}</div>
<div className="flex gap-3 items-center justify-end"> <div className="flex gap-3 items-center justify-start">
<StatusItem <StatusItem
condition={user.hasPassword} condition={user.hasPassword}
IconTrue={<LockIcon size={10} />} IconTrue={
IconFalse={<UnlockIcon size={10} />} <LockIcon
fontSize={16}
color={cssVarV2('selfhost/icon/tertiary')}
/>
}
IconFalse={<UnlockIcon fontSize={16} />}
textTrue="Password Set" textTrue="Password Set"
textFalse="No Password" textFalse="No Password"
/> />
<StatusItem <StatusItem
condition={user.emailVerified} condition={user.emailVerified}
IconTrue={<MailIcon size={10} />} IconTrue={
IconFalse={<MailWarningIcon size={10} />} <MailIcon
size={16}
color={cssVarV2('selfhost/icon/tertiary')}
/>
}
IconFalse={<MailIcon size={16} />}
textTrue="Email Verified" textTrue="Email Verified"
textFalse="Email Not Verified" textFalse="Email Not Verified"
/> />
@@ -170,25 +170,21 @@ export function DataTableRowActions({ user }: DataTableRowActionsProps) {
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[214px] p-[5px] gap-2"> <DropdownMenuContent align="end" className="w-[214px] p-[5px] gap-2">
<div className="px-2 py-[6px] text-sm font-semibold overflow-hidden text-ellipsis text-nowrap">
{user.name}
</div>
<DropdownMenuSeparator />
<DropdownMenuItem <DropdownMenuItem
className="px-2 py-[6px] text-sm font-medium gap-2 cursor-pointer" className="px-2 py-[6px] text-sm font-normal gap-2 cursor-pointer"
onSelect={openResetPasswordDialog} onSelect={openResetPasswordDialog}
> >
<LockIcon fontSize={20} /> Reset Password <LockIcon fontSize={20} /> Reset Password
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onSelect={handleEdit} onSelect={handleEdit}
className="px-2 py-[6px] text-sm font-medium gap-2 cursor-pointer" className="px-2 py-[6px] text-sm font-normal gap-2 cursor-pointer"
> >
<EditIcon fontSize={20} /> Edit <EditIcon fontSize={20} /> Edit
</DropdownMenuItem> </DropdownMenuItem>
{user.disabled && ( {user.disabled && (
<DropdownMenuItem <DropdownMenuItem
className="px-2 py-[6px] text-sm font-medium gap-2 cursor-pointer" className="px-2 py-[6px] text-sm font-normal gap-2 cursor-pointer"
onSelect={openEnableDialog} onSelect={openEnableDialog}
> >
<AccountBanIcon fontSize={20} /> Enable Email <AccountBanIcon fontSize={20} /> Enable Email
@@ -197,14 +193,14 @@ export function DataTableRowActions({ user }: DataTableRowActionsProps) {
<DropdownMenuSeparator /> <DropdownMenuSeparator />
{!user.disabled && ( {!user.disabled && (
<DropdownMenuItem <DropdownMenuItem
className="px-2 py-[6px] text-sm font-medium gap-2 text-red-500 cursor-pointer focus:text-red-500" className="px-2 py-[6px] text-sm font-normal gap-2 text-red-500 cursor-pointer focus:text-red-500"
onSelect={openDisableDialog} onSelect={openDisableDialog}
> >
<AccountBanIcon fontSize={20} /> Disable & Delete data <AccountBanIcon fontSize={20} /> Disable & Delete data
</DropdownMenuItem> </DropdownMenuItem>
)} )}
<DropdownMenuItem <DropdownMenuItem
className="px-2 py-[6px] text-sm font-medium gap-2 text-red-500 cursor-pointer focus:text-red-500" className="px-2 py-[6px] text-sm font-normal gap-2 text-red-500 cursor-pointer focus:text-red-500"
onSelect={openDeleteDialog} onSelect={openDeleteDialog}
> >
<DeleteIcon fontSize={20} /> Delete <DeleteIcon fontSize={20} /> Delete
@@ -69,9 +69,9 @@ export function DataTable<TData, TValue>({
}, [data]); }, [data]);
return ( return (
<div className="flex flex-col gap-4 py-5 px-6 h-full"> <div className="flex flex-col gap-4 py-5 px-6 h-full overflow-auto">
<DataTableToolbar setDataTable={setTableData} data={data} table={table} /> <DataTableToolbar setDataTable={setTableData} data={data} table={table} />
<div className="rounded-md border max-h-[75vh] h-full overflow-auto"> <div className="rounded-md border h-full flex flex-col overflow-auto">
<Table> <Table>
<TableHeader> <TableHeader>
{table.getHeaderGroups().map(headerGroup => ( {table.getHeaderGroups().map(headerGroup => (
@@ -107,49 +107,54 @@ export function DataTable<TData, TValue>({
</TableRow> </TableRow>
))} ))}
</TableHeader> </TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map(row => (
<TableRow key={row.id} className="flex items-center">
{row.getVisibleCells().map(cell => {
let columnClassName = '';
if (cell.column.id === 'select') {
columnClassName = 'w-[40px] flex-shrink-0';
} else if (cell.column.id === 'info') {
columnClassName = 'flex-1';
} else if (cell.column.id === 'property') {
columnClassName = 'flex-1';
} else if (cell.column.id === 'actions') {
columnClassName =
'w-[40px] flex-shrink-0 justify-center mr-6';
}
return (
<TableCell
key={cell.id}
className={`${columnClassName} flex items-center`}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
);
})}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table> </Table>
<div className="overflow-auto flex-1">
<Table>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map(row => (
<TableRow key={row.id} className="flex items-center">
{row.getVisibleCells().map(cell => {
let columnClassName = '';
if (cell.column.id === 'select') {
columnClassName = 'w-[40px] flex-shrink-0';
} else if (cell.column.id === 'info') {
columnClassName = 'flex-1';
} else if (cell.column.id === 'property') {
columnClassName = 'flex-1';
} else if (cell.column.id === 'actions') {
columnClassName =
'w-[40px] flex-shrink-0 justify-center mr-6';
}
return (
<TableCell
key={cell.id}
className={`${columnClassName} flex items-center`}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
);
})}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
</div> </div>
<DataTablePagination table={table} /> <DataTablePagination table={table} />
@@ -56,7 +56,7 @@ export const DeleteAccountDialog = ({
className="placeholder:opacity-50" className="placeholder:opacity-50"
/> />
<DialogFooter> <DialogFooter>
<div className="flex justify-between items-center w-full"> <div className="flex justify-end gap-2 items-center w-full">
<Button type="button" variant="outline" size="sm" onClick={onClose}> <Button type="button" variant="outline" size="sm" onClick={onClose}>
Cancel Cancel
</Button> </Button>
@@ -56,7 +56,7 @@ export const DisableAccountDialog = ({
className="placeholder:opacity-50" className="placeholder:opacity-50"
/> />
<DialogFooter> <DialogFooter>
<div className="flex justify-between items-center w-full"> <div className="flex justify-end gap-2 items-center w-full">
<Button type="button" variant="outline" size="sm" onClick={onClose}> <Button type="button" variant="outline" size="sm" onClick={onClose}>
Cancel Cancel
</Button> </Button>
@@ -23,13 +23,13 @@ export const DiscardChanges = ({
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:w-[460px]"> <DialogContent className="sm:w-[460px]">
<DialogHeader> <DialogHeader>
<DialogTitle className="leading-7">Discard Changes</DialogTitle> <DialogTitle className="leading-6.5">Discard Changes</DialogTitle>
<DialogDescription className="leading-6"> <DialogDescription className="leading-6 text-[15px]">
Changes to this user will not be saved. Changes to this user will not be saved.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogFooter> <DialogFooter>
<div className="flex justify-end items-center w-full space-x-4"> <div className="flex justify-end gap-2 items-center w-full">
<Button type="button" onClick={onClose} variant="outline"> <Button type="button" onClick={onClose} variant="outline">
<span>Cancel</span> <span>Cancel</span>
</Button> </Button>
@@ -33,7 +33,7 @@ export const EnableAccountDialog = ({
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogFooter> <DialogFooter>
<div className="flex justify-end items-center w-full space-x-4"> <div className="flex justify-end gap-2 items-center w-full">
<Button type="button" onClick={onClose} variant="outline"> <Button type="button" onClick={onClose} variant="outline">
<span>Cancel</span> <span>Cancel</span>
</Button> </Button>
@@ -2,6 +2,7 @@ import { Button } from '@affine/admin/components/ui/button';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogDescription,
DialogFooter, DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
@@ -198,7 +199,9 @@ export function ImportUsersDialog({
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent <DialogContent
className={isPreviewMode ? 'sm:max-w-[600px]' : 'sm:max-w-[425px]'} className={
isPreviewMode ? 'sm:max-w-[600px] flex-col gap-3' : 'sm:max-w-[425px]'
}
> >
<DialogHeader> <DialogHeader>
<DialogTitle> <DialogTitle>
@@ -211,35 +214,32 @@ export function ImportUsersDialog({
: 'Import'} : 'Import'}
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
<DialogDescription className="text-[15px]">
{isFormatError ? ( {isFormatError ? (
<div className="grid gap-4 py-4"> 'You need to import the accounts by importing a CSV file in the correct format. Please download the CSV template.'
<p className="text-sm text-gray-500"> ) : isPreviewMode ? (
You need to import the accounts by importing a CSV file in the <div className="grid gap-3">
correct format. Please download the CSV template. {isImported ? null : (
</p> <p>
</div> {parsedUsers.length} users detected from the CSV file. Please
) : isPreviewMode ? ( confirm the user list below and import.
<div className="grid gap-4 py-4"> </p>
<p className="text-sm text-gray-500"> )}
{parsedUsers.length} users detected from the CSV file. Please <UserTable users={parsedUsers} />
confirm the user list below and import. </div>
</p> ) : (
<UserTable users={parsedUsers} /> <div className="grid gap-3">
</div> <p>
) : ( You need to import the accounts by importing a CSV file in the
<div className="grid gap-4 py-4"> correct format. Please download the CSV template.
<p className="text-sm text-gray-500"> </p>
You need to import the accounts by importing a CSV file in the <FileUploadArea
correct format. Please download the CSV template. ref={fileUploadRef}
</p> onFileSelected={handleFileSelected}
/>
<FileUploadArea </div>
ref={fileUploadRef} )}
onFileSelected={handleFileSelected} </DialogDescription>
/>
</div>
)}
<DialogFooter <DialogFooter
className={`flex-col sm:flex-row sm:justify-between items-center ${isPreviewMode ? 'sm:justify-end' : 'sm:justify-between'}`} className={`flex-col sm:flex-row sm:justify-between items-center ${isPreviewMode ? 'sm:justify-end' : 'sm:justify-between'}`}
@@ -265,9 +265,8 @@ export function ImportUsersDialog({
<> <>
<Button <Button
variant="outline" variant="outline"
size="sm"
onClick={cancelImport} onClick={cancelImport}
className="mb-2 sm:mb-0" className="w-full mb-2 sm:mb-0 sm:w-auto"
disabled={isImporting} disabled={isImporting}
> >
Cancel Cancel
@@ -32,7 +32,7 @@ export const ResetPasswordDialog = ({
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogFooter> <DialogFooter>
<div className="flex justify-between items-center w-full space-x-4"> <div className="flex justify-end gap-2 items-center w-full">
<Input <Input
type="text" type="text"
value={link} value={link}
@@ -29,7 +29,7 @@ export const DiscardChanges = ({
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogFooter> <DialogFooter>
<div className="flex justify-end items-center w-full space-x-4"> <div className="flex justify-end gap-2 items-center w-full">
<Button type="button" onClick={onClose} variant="outline"> <Button type="button" onClick={onClose} variant="outline">
<span>Cancel</span> <span>Cancel</span>
</Button> </Button>
+12 -5
View File
@@ -216,7 +216,7 @@ export const LeftPanel = ({
onExpand={onExpand} onExpand={onExpand}
onCollapse={onCollapse} onCollapse={onCollapse}
className={cn( className={cn(
isCollapsed ? 'min-w-[56px] max-w-[56px]' : 'min-w-56 max-w-56', isCollapsed ? 'min-w-[57px] max-w-[57px]' : 'min-w-56 max-w-56',
'border-r h-dvh' 'border-r h-dvh'
)} )}
style={{ overflow: 'visible' }} style={{ overflow: 'visible' }}
@@ -231,11 +231,18 @@ export const LeftPanel = ({
> >
<div <div
className={cn( className={cn(
'flex h-[56px] items-center gap-2 px-4 text-base font-medium', 'flex h-[56px] items-center px-4 text-base font-medium',
isCollapsed && 'justify-center px-2' isCollapsed && 'justify-center px-2'
)} )}
> >
<Logo /> <span
className={cn(
'flex items-center p-0.5 mr-2',
isCollapsed && 'justify-center px-2 mr-0'
)}
>
<Logo />
</span>
{!isCollapsed && 'AFFiNE'} {!isCollapsed && 'AFFiNE'}
</div> </div>
<Nav isCollapsed={isCollapsed} /> <Nav isCollapsed={isCollapsed} />
@@ -283,12 +290,12 @@ export const RightPanel = ({
order={2} order={2}
ref={panelRef} ref={panelRef}
defaultSize={0} defaultSize={0}
maxSize={30} maxSize={20}
collapsible={true} collapsible={true}
collapsedSize={0} collapsedSize={0}
onExpand={onExpand} onExpand={onExpand}
onCollapse={onCollapse} onCollapse={onCollapse}
className="border-l" className="border-l max-w-96"
> >
{panelContent} {panelContent}
</ResizablePanel> </ResizablePanel>
@@ -60,7 +60,7 @@ const NavItem = ({ icon, label, to, isCollapsed }: NavItemProps) => {
}, },
})} })}
> >
{icon} <span className="flex items-center p-0.5 mr-2">{icon}</span>
{label} {label}
</NavLink> </NavLink>
); );
@@ -86,25 +86,19 @@ export function Nav({ isCollapsed = false }: NavProps) {
> >
<NavItem <NavItem
to="/admin/config" to="/admin/config"
icon={ icon={<SelfhostIcon fontSize={20} />}
<SelfhostIcon className={cn(!isCollapsed && 'mr-2', 'h-5 w-5')} />
}
label="Server" label="Server"
isCollapsed={isCollapsed} isCollapsed={isCollapsed}
/> />
<NavItem <NavItem
to="/admin/accounts" to="/admin/accounts"
icon={ icon={<AccountIcon fontSize={20} />}
<AccountIcon className={cn(!isCollapsed && 'mr-2', 'h-5 w-5')} />
}
label="Accounts" label="Accounts"
isCollapsed={isCollapsed} isCollapsed={isCollapsed}
/> />
<NavItem <NavItem
to="/admin/ai" to="/admin/ai"
icon={ icon={<AiOutlineIcon fontSize={20} />}
<AiOutlineIcon className={cn(!isCollapsed && 'mr-2', 'h-5 w-5')} />
}
label="AI" label="AI"
isCollapsed={isCollapsed} isCollapsed={isCollapsed}
/> />
@@ -17,10 +17,15 @@ export const ServerVersion = () => {
return ( return (
<Button <Button
variant="outline" variant="outline"
className="flex items-center justify-center gap-1 text-xs p-2 font-medium" className="flex items-center justify-center gap-1 text-xs p-2 font-medium w-full overflow-hidden"
onClick={handleClick} onClick={handleClick}
title={`New Version ${availableUpgrade.version} Available`}
> >
New Version <span>{availableUpgrade.version}</span>Available <span className="overflow-hidden text-ellipsis">
New Version
<span>{availableUpgrade.version}</span>
Available
</span>
</Button> </Button>
); );
} }
@@ -52,7 +52,7 @@ export const SettingsItem = ({ isCollapsed }: { isCollapsed: boolean }) => {
: undefined, : undefined,
})} })}
> >
<SettingsIcon className="h-5 w-5" /> <SettingsIcon fontSize={20} />
</NavLink> </NavLink>
</NavigationMenuPrimitive.Trigger> </NavigationMenuPrimitive.Trigger>
<NavigationMenuPrimitive.Content> <NavigationMenuPrimitive.Content>
@@ -119,7 +119,9 @@ export const SettingsItem = ({ isCollapsed }: { isCollapsed: boolean }) => {
} }
> >
<div className="flex items-center"> <div className="flex items-center">
<SettingsIcon className={cn(!isCollapsed && 'mr-2', 'h-5 w-5')} /> <span className="flex items-center p-0.5 mr-2">
<SettingsIcon fontSize={20} />
</span>
<span>Settings</span> <span>Settings</span>
</div> </div>
</AccordionTrigger> </AccordionTrigger>
@@ -73,15 +73,20 @@ export function UserDropdown({ isCollapsed }: UserDropdownProps) {
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
{currentUser?.name ? ( {currentUser?.name ? (
<span className="text-sm text-nowrap text-ellipsis break-words overflow-hidden"> <span
className="text-sm text-nowrap text-ellipsis break-words overflow-hidden"
title={currentUser?.name}
>
{currentUser?.name} {currentUser?.name}
</span> </span>
) : ( ) : (
// Fallback to email prefix if name is not available // Fallback to email prefix if name is not available
<span className="text-sm">{currentUser?.email.split('@')[0]}</span> <span className="text-sm" title={currentUser?.email.split('@')[0]}>
{currentUser?.email.split('@')[0]}
</span>
)} )}
<span <span
className="ml-2 rounded px-2 py-0.5 text-xs h-5 border" className="ml-2 rounded px-2 py-0.5 text-xs h-5 border text-center inline-flex items-center font-normal"
style={{ style={{
borderRadius: '4px', borderRadius: '4px',
backgroundColor: cssVarV2('chip/label/blue'), backgroundColor: cssVarV2('chip/label/blue'),
@@ -98,7 +103,27 @@ export function UserDropdown({ isCollapsed }: UserDropdownProps) {
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" side="right"> <DropdownMenuContent align="end" side="right">
<DropdownMenuLabel>{currentUser?.name}</DropdownMenuLabel> <DropdownMenuLabel className="flex items-center gap-2">
<Avatar className="w-8 h-8">
<AvatarImage src={currentUser?.avatarUrl ?? undefined} />
<AvatarFallback>
<CircleUser size={32} />
</AvatarFallback>
</Avatar>
<div className="flex flex-col font-medium gap-1">
{currentUser?.name ?? currentUser?.email.split('@')[0]}
<span
className="w-fit rounded px-2 py-0.5 text-xs h-5 border text-center inline-flex items-center font-normal"
style={{
borderRadius: '4px',
backgroundColor: cssVarV2('chip/label/blue'),
borderColor: cssVarV2('layer/insideBorder/border'),
}}
>
Admin
</span>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onSelect={handleLogout}>Logout</DropdownMenuItem> <DropdownMenuItem onSelect={handleLogout}>Logout</DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
@@ -66,7 +66,7 @@ export const ConfirmChanges = ({
'There is no change.' 'There is no change.'
)} )}
<DialogFooter> <DialogFooter>
<div className="flex justify-end items-center w-full space-x-4"> <div className="flex justify-end items-center w-full gap-2">
<Button type="button" onClick={onClose} variant="outline"> <Button type="button" onClick={onClose} variant="outline">
<span>Cancel</span> <span>Cancel</span>
</Button> </Button>