mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 12:55:00 +00:00
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:
@@ -5,16 +5,10 @@ import {
|
||||
} from '@affine/admin/components/ui/avatar';
|
||||
import type { UserType } from '@affine/graphql';
|
||||
import { FeatureType } from '@affine/graphql';
|
||||
import { AccountIcon, LockIcon, UnlockIcon } from '@blocksuite/icons/rc';
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
LockIcon,
|
||||
MailIcon,
|
||||
MailWarningIcon,
|
||||
UnlockIcon,
|
||||
UserIcon,
|
||||
} from 'lucide-react';
|
||||
import { MailIcon } from 'lucide-react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import { Checkbox } from '../../../components/ui/checkbox';
|
||||
@@ -35,10 +29,10 @@ const StatusItem = ({
|
||||
textFalse: string;
|
||||
}) => (
|
||||
<div
|
||||
className={clsx(
|
||||
'flex gap-2 items-center',
|
||||
!condition ? 'text-red-500 opacity-100' : 'opacity-25'
|
||||
)}
|
||||
className="flex gap-1 items-center"
|
||||
style={{
|
||||
color: condition ? cssVarV2('text/secondary') : cssVarV2('status/error'),
|
||||
}}
|
||||
>
|
||||
{condition ? (
|
||||
<>
|
||||
@@ -89,7 +83,7 @@ export const columns: ColumnDef<UserType>[] = [
|
||||
<Avatar className="w-10 h-10">
|
||||
<AvatarImage src={row.original.avatarUrl ?? undefined} />
|
||||
<AvatarFallback>
|
||||
<UserIcon size={20} />
|
||||
<AccountIcon fontSize={20} />
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<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>
|
||||
{row.original.features.includes(FeatureType.Admin) && (
|
||||
<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={{
|
||||
borderRadius: '4px',
|
||||
backgroundColor: cssVarV2('chip/label/blue'),
|
||||
@@ -120,7 +114,12 @@ export const columns: ColumnDef<UserType>[] = [
|
||||
</span>
|
||||
)}
|
||||
</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}
|
||||
</div>
|
||||
</div>
|
||||
@@ -141,20 +140,29 @@ export const columns: ColumnDef<UserType>[] = [
|
||||
cell: ({ row: { original: user } }) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<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 gap-3 items-center justify-end">
|
||||
<div className="flex justify-start">{user.id}</div>
|
||||
<div className="flex gap-3 items-center justify-start">
|
||||
<StatusItem
|
||||
condition={user.hasPassword}
|
||||
IconTrue={<LockIcon size={10} />}
|
||||
IconFalse={<UnlockIcon size={10} />}
|
||||
IconTrue={
|
||||
<LockIcon
|
||||
fontSize={16}
|
||||
color={cssVarV2('selfhost/icon/tertiary')}
|
||||
/>
|
||||
}
|
||||
IconFalse={<UnlockIcon fontSize={16} />}
|
||||
textTrue="Password Set"
|
||||
textFalse="No Password"
|
||||
/>
|
||||
|
||||
<StatusItem
|
||||
condition={user.emailVerified}
|
||||
IconTrue={<MailIcon size={10} />}
|
||||
IconFalse={<MailWarningIcon size={10} />}
|
||||
IconTrue={
|
||||
<MailIcon
|
||||
size={16}
|
||||
color={cssVarV2('selfhost/icon/tertiary')}
|
||||
/>
|
||||
}
|
||||
IconFalse={<MailIcon size={16} />}
|
||||
textTrue="Email Verified"
|
||||
textFalse="Email Not Verified"
|
||||
/>
|
||||
|
||||
@@ -170,25 +170,21 @@ export function DataTableRowActions({ user }: DataTableRowActionsProps) {
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<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
|
||||
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}
|
||||
>
|
||||
<LockIcon fontSize={20} /> Reset Password
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
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
|
||||
</DropdownMenuItem>
|
||||
{user.disabled && (
|
||||
<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}
|
||||
>
|
||||
<AccountBanIcon fontSize={20} /> Enable Email
|
||||
@@ -197,14 +193,14 @@ export function DataTableRowActions({ user }: DataTableRowActionsProps) {
|
||||
<DropdownMenuSeparator />
|
||||
{!user.disabled && (
|
||||
<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}
|
||||
>
|
||||
<AccountBanIcon fontSize={20} /> Disable & Delete data
|
||||
</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}
|
||||
>
|
||||
<DeleteIcon fontSize={20} /> Delete
|
||||
|
||||
@@ -69,9 +69,9 @@ export function DataTable<TData, TValue>({
|
||||
}, [data]);
|
||||
|
||||
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} />
|
||||
<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>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
@@ -107,49 +107,54 @@ export function DataTable<TData, TValue>({
|
||||
</TableRow>
|
||||
))}
|
||||
</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>
|
||||
|
||||
<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>
|
||||
|
||||
<DataTablePagination table={table} />
|
||||
|
||||
@@ -56,7 +56,7 @@ export const DeleteAccountDialog = ({
|
||||
className="placeholder:opacity-50"
|
||||
/>
|
||||
<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}>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
@@ -56,7 +56,7 @@ export const DisableAccountDialog = ({
|
||||
className="placeholder:opacity-50"
|
||||
/>
|
||||
<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}>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
@@ -23,13 +23,13 @@ export const DiscardChanges = ({
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:w-[460px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="leading-7">Discard Changes</DialogTitle>
|
||||
<DialogDescription className="leading-6">
|
||||
<DialogTitle className="leading-6.5">Discard Changes</DialogTitle>
|
||||
<DialogDescription className="leading-6 text-[15px]">
|
||||
Changes to this user will not be saved.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<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">
|
||||
<span>Cancel</span>
|
||||
</Button>
|
||||
|
||||
@@ -33,7 +33,7 @@ export const EnableAccountDialog = ({
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<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">
|
||||
<span>Cancel</span>
|
||||
</Button>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Button } from '@affine/admin/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
@@ -198,7 +199,9 @@ export function ImportUsersDialog({
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<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>
|
||||
<DialogTitle>
|
||||
@@ -211,35 +214,32 @@ export function ImportUsersDialog({
|
||||
: 'Import'}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
{isFormatError ? (
|
||||
<div className="grid gap-4 py-4">
|
||||
<p className="text-sm text-gray-500">
|
||||
You need to import the accounts by importing a CSV file in the
|
||||
correct format. Please download the CSV template.
|
||||
</p>
|
||||
</div>
|
||||
) : isPreviewMode ? (
|
||||
<div className="grid gap-4 py-4">
|
||||
<p className="text-sm text-gray-500">
|
||||
{parsedUsers.length} users detected from the CSV file. Please
|
||||
confirm the user list below and import.
|
||||
</p>
|
||||
<UserTable users={parsedUsers} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-4 py-4">
|
||||
<p className="text-sm text-gray-500">
|
||||
You need to import the accounts by importing a CSV file in the
|
||||
correct format. Please download the CSV template.
|
||||
</p>
|
||||
|
||||
<FileUploadArea
|
||||
ref={fileUploadRef}
|
||||
onFileSelected={handleFileSelected}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<DialogDescription className="text-[15px]">
|
||||
{isFormatError ? (
|
||||
'You need to import the accounts by importing a CSV file in the correct format. Please download the CSV template.'
|
||||
) : isPreviewMode ? (
|
||||
<div className="grid gap-3">
|
||||
{isImported ? null : (
|
||||
<p>
|
||||
{parsedUsers.length} users detected from the CSV file. Please
|
||||
confirm the user list below and import.
|
||||
</p>
|
||||
)}
|
||||
<UserTable users={parsedUsers} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-3">
|
||||
<p>
|
||||
You need to import the accounts by importing a CSV file in the
|
||||
correct format. Please download the CSV template.
|
||||
</p>
|
||||
<FileUploadArea
|
||||
ref={fileUploadRef}
|
||||
onFileSelected={handleFileSelected}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</DialogDescription>
|
||||
|
||||
<DialogFooter
|
||||
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
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={cancelImport}
|
||||
className="mb-2 sm:mb-0"
|
||||
className="w-full mb-2 sm:mb-0 sm:w-auto"
|
||||
disabled={isImporting}
|
||||
>
|
||||
Cancel
|
||||
|
||||
@@ -32,7 +32,7 @@ export const ResetPasswordDialog = ({
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<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
|
||||
type="text"
|
||||
value={link}
|
||||
|
||||
@@ -29,7 +29,7 @@ export const DiscardChanges = ({
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<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">
|
||||
<span>Cancel</span>
|
||||
</Button>
|
||||
|
||||
@@ -216,7 +216,7 @@ export const LeftPanel = ({
|
||||
onExpand={onExpand}
|
||||
onCollapse={onCollapse}
|
||||
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'
|
||||
)}
|
||||
style={{ overflow: 'visible' }}
|
||||
@@ -231,11 +231,18 @@ export const LeftPanel = ({
|
||||
>
|
||||
<div
|
||||
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'
|
||||
)}
|
||||
>
|
||||
<Logo />
|
||||
<span
|
||||
className={cn(
|
||||
'flex items-center p-0.5 mr-2',
|
||||
isCollapsed && 'justify-center px-2 mr-0'
|
||||
)}
|
||||
>
|
||||
<Logo />
|
||||
</span>
|
||||
{!isCollapsed && 'AFFiNE'}
|
||||
</div>
|
||||
<Nav isCollapsed={isCollapsed} />
|
||||
@@ -283,12 +290,12 @@ export const RightPanel = ({
|
||||
order={2}
|
||||
ref={panelRef}
|
||||
defaultSize={0}
|
||||
maxSize={30}
|
||||
maxSize={20}
|
||||
collapsible={true}
|
||||
collapsedSize={0}
|
||||
onExpand={onExpand}
|
||||
onCollapse={onCollapse}
|
||||
className="border-l"
|
||||
className="border-l max-w-96"
|
||||
>
|
||||
{panelContent}
|
||||
</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}
|
||||
</NavLink>
|
||||
);
|
||||
@@ -86,25 +86,19 @@ export function Nav({ isCollapsed = false }: NavProps) {
|
||||
>
|
||||
<NavItem
|
||||
to="/admin/config"
|
||||
icon={
|
||||
<SelfhostIcon className={cn(!isCollapsed && 'mr-2', 'h-5 w-5')} />
|
||||
}
|
||||
icon={<SelfhostIcon fontSize={20} />}
|
||||
label="Server"
|
||||
isCollapsed={isCollapsed}
|
||||
/>
|
||||
<NavItem
|
||||
to="/admin/accounts"
|
||||
icon={
|
||||
<AccountIcon className={cn(!isCollapsed && 'mr-2', 'h-5 w-5')} />
|
||||
}
|
||||
icon={<AccountIcon fontSize={20} />}
|
||||
label="Accounts"
|
||||
isCollapsed={isCollapsed}
|
||||
/>
|
||||
<NavItem
|
||||
to="/admin/ai"
|
||||
icon={
|
||||
<AiOutlineIcon className={cn(!isCollapsed && 'mr-2', 'h-5 w-5')} />
|
||||
}
|
||||
icon={<AiOutlineIcon fontSize={20} />}
|
||||
label="AI"
|
||||
isCollapsed={isCollapsed}
|
||||
/>
|
||||
|
||||
@@ -17,10 +17,15 @@ export const ServerVersion = () => {
|
||||
return (
|
||||
<Button
|
||||
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}
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export const SettingsItem = ({ isCollapsed }: { isCollapsed: boolean }) => {
|
||||
: undefined,
|
||||
})}
|
||||
>
|
||||
<SettingsIcon className="h-5 w-5" />
|
||||
<SettingsIcon fontSize={20} />
|
||||
</NavLink>
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
<NavigationMenuPrimitive.Content>
|
||||
@@ -119,7 +119,9 @@ export const SettingsItem = ({ isCollapsed }: { isCollapsed: boolean }) => {
|
||||
}
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
|
||||
@@ -73,15 +73,20 @@ export function UserDropdown({ isCollapsed }: UserDropdownProps) {
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
{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}
|
||||
</span>
|
||||
) : (
|
||||
// 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
|
||||
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={{
|
||||
borderRadius: '4px',
|
||||
backgroundColor: cssVarV2('chip/label/blue'),
|
||||
@@ -98,7 +103,27 @@ export function UserDropdown({ isCollapsed }: UserDropdownProps) {
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<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 />
|
||||
<DropdownMenuItem onSelect={handleLogout}>Logout</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
||||
@@ -66,7 +66,7 @@ export const ConfirmChanges = ({
|
||||
'There is no change.'
|
||||
)}
|
||||
<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">
|
||||
<span>Cancel</span>
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user