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';
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}