mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-24 13:58:50 +08:00
feat: improve admin panel design (#14464)
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
} from '@affine/admin/components/ui/avatar';
|
||||
import { cn } from '@affine/admin/utils';
|
||||
import { FeatureType } from '@affine/graphql';
|
||||
import {
|
||||
AccountIcon,
|
||||
@@ -12,7 +13,6 @@ import {
|
||||
UnlockIcon,
|
||||
} from '@blocksuite/icons/rc';
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import {
|
||||
type Dispatch,
|
||||
type ReactNode,
|
||||
@@ -39,10 +39,10 @@ const StatusItem = ({
|
||||
textFalse: string;
|
||||
}) => (
|
||||
<div
|
||||
className="flex gap-1 items-center"
|
||||
style={{
|
||||
color: condition ? cssVarV2('text/secondary') : cssVarV2('status/error'),
|
||||
}}
|
||||
className={cn(
|
||||
'flex items-center gap-1',
|
||||
condition ? 'text-muted-foreground' : 'text-destructive'
|
||||
)}
|
||||
>
|
||||
{condition ? (
|
||||
<>
|
||||
@@ -152,36 +152,17 @@ export const useColumns = ({
|
||||
<div className="text-sm font-medium max-w-full overflow-hidden gap-[6px]">
|
||||
<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 text-center inline-flex items-center font-normal"
|
||||
style={{
|
||||
borderRadius: '4px',
|
||||
backgroundColor: cssVarV2('chip/label/blue'),
|
||||
borderColor: cssVarV2('layer/insideBorder/border'),
|
||||
}}
|
||||
>
|
||||
<span className="ml-2 inline-flex h-5 items-center rounded-md border border-border/60 bg-chip-blue px-2 py-0.5 text-xxs font-medium text-chip-text">
|
||||
Admin
|
||||
</span>
|
||||
)}
|
||||
{row.original.disabled && (
|
||||
<span
|
||||
className="ml-2 rounded px-2 py-0.5 text-xs h-5 border"
|
||||
style={{
|
||||
borderRadius: '4px',
|
||||
backgroundColor: cssVarV2('chip/label/white'),
|
||||
borderColor: cssVarV2('layer/insideBorder/border'),
|
||||
}}
|
||||
>
|
||||
<span className="ml-2 inline-flex h-5 items-center rounded-md border border-border/60 bg-chip-white px-2 py-0.5 text-xxs font-medium">
|
||||
Disabled
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="text-xs font-medium max-w-full overflow-hidden"
|
||||
style={{
|
||||
color: cssVarV2('text/secondary'),
|
||||
}}
|
||||
>
|
||||
<div className="max-w-full overflow-hidden text-xs font-medium text-muted-foreground">
|
||||
{row.original.email}
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,16 +188,10 @@ export const useColumns = ({
|
||||
<StatusItem
|
||||
condition={user.hasPassword}
|
||||
IconTrue={
|
||||
<LockIcon
|
||||
fontSize={16}
|
||||
color={cssVarV2('selfhost/icon/tertiary')}
|
||||
/>
|
||||
<LockIcon fontSize={16} className="text-muted-foreground" />
|
||||
}
|
||||
IconFalse={
|
||||
<UnlockIcon
|
||||
fontSize={16}
|
||||
color={cssVarV2('toast/iconState/error')}
|
||||
/>
|
||||
<UnlockIcon fontSize={16} className="text-destructive" />
|
||||
}
|
||||
textTrue="Password Set"
|
||||
textFalse="No Password"
|
||||
@@ -226,13 +201,13 @@ export const useColumns = ({
|
||||
IconTrue={
|
||||
<EmailIcon
|
||||
fontSize={16}
|
||||
color={cssVarV2('selfhost/icon/tertiary')}
|
||||
className="text-muted-foreground"
|
||||
/>
|
||||
}
|
||||
IconFalse={
|
||||
<EmailWarningIcon
|
||||
fontSize={16}
|
||||
color={cssVarV2('toast/iconState/error')}
|
||||
className="text-destructive"
|
||||
/>
|
||||
}
|
||||
textTrue="Email Verified"
|
||||
@@ -244,24 +219,13 @@ export const useColumns = ({
|
||||
user.features.map(feature => (
|
||||
<span
|
||||
key={feature}
|
||||
className="rounded px-2 py-0.5 text-xs h-5 border inline-flex items-center"
|
||||
style={{
|
||||
borderRadius: '4px',
|
||||
backgroundColor: cssVarV2('chip/label/white'),
|
||||
borderColor: cssVarV2('layer/insideBorder/border'),
|
||||
}}
|
||||
className="inline-flex h-5 items-center rounded-md border border-border/60 bg-chip-white px-2 py-0.5 text-xxs font-medium"
|
||||
>
|
||||
{feature}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
<span
|
||||
style={{
|
||||
color: cssVarV2('text/secondary'),
|
||||
}}
|
||||
>
|
||||
No features
|
||||
</span>
|
||||
<span className="text-muted-foreground">No features</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -168,13 +168,14 @@ export function DataTableRowActions({ user }: DataTableRowActionsProps) {
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="flex h-8 w-8 p-0 data-[state=open]:bg-muted"
|
||||
className="flex h-8 w-8 p-0 data-[state=open]:bg-accent"
|
||||
size="icon"
|
||||
>
|
||||
<MoreHorizontalIcon fontSize={20} />
|
||||
<span className="sr-only">Open menu</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-[214px] p-[5px] gap-2">
|
||||
<DropdownMenuContent align="end" className="w-[214px] p-1.5">
|
||||
<DropdownMenuItem
|
||||
onSelect={handleEdit}
|
||||
className="px-2 py-[6px] text-sm font-normal gap-2 cursor-pointer"
|
||||
@@ -201,7 +202,7 @@ export function DataTableRowActions({ user }: DataTableRowActionsProps) {
|
||||
<DropdownMenuSeparator />
|
||||
{!user.disabled && (
|
||||
<DropdownMenuItem
|
||||
className="px-2 py-[6px] text-sm font-normal gap-2 text-red-500 cursor-pointer focus:text-red-500"
|
||||
className="cursor-pointer gap-2 px-2 py-[6px] text-sm font-normal text-destructive focus:text-destructive"
|
||||
onSelect={openDisableDialog}
|
||||
>
|
||||
<AccountBanIcon fontSize={20} />
|
||||
@@ -209,7 +210,7 @@ export function DataTableRowActions({ user }: DataTableRowActionsProps) {
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
className="px-2 py-[6px] text-sm font-normal gap-2 text-red-500 cursor-pointer focus:text-red-500"
|
||||
className="cursor-pointer gap-2 px-2 py-[6px] text-sm font-normal text-destructive focus:text-destructive"
|
||||
onSelect={openDeleteDialog}
|
||||
>
|
||||
<DeleteIcon fontSize={20} />
|
||||
|
||||
+2
-11
@@ -1,6 +1,4 @@
|
||||
import { WarningIcon } from '@blocksuite/icons/rc';
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import type { FC } from 'react';
|
||||
|
||||
interface CsvFormatGuidanceProps {
|
||||
@@ -17,16 +15,9 @@ export const CsvFormatGuidance: FC<CsvFormatGuidanceProps> = ({
|
||||
passwordLimits,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className="flex p-1.5 gap-1 rounded-[6px]"
|
||||
style={{
|
||||
fontSize: cssVar('fontXs'),
|
||||
color: cssVarV2('text/secondary'),
|
||||
backgroundColor: cssVarV2('layer/background/secondary'),
|
||||
}}
|
||||
>
|
||||
<div className="flex gap-1 rounded-[6px] bg-secondary p-1.5 text-xs text-muted-foreground">
|
||||
<div className="flex justify-center py-0.5">
|
||||
<WarningIcon fontSize={16} color={cssVarV2('icon/primary')} />
|
||||
<WarningIcon fontSize={16} className="text-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<p>CSV file includes username, email, and password.</p>
|
||||
|
||||
+7
-17
@@ -1,6 +1,5 @@
|
||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||
import { UploadIcon } from '@blocksuite/icons/rc';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import {
|
||||
type ChangeEvent,
|
||||
type DragEvent,
|
||||
@@ -86,37 +85,28 @@ export const FileUploadArea = forwardRef<
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex justify-center p-8 border-2 border-dashed rounded-[6px] ${
|
||||
isDragging ? 'border-blue-500 bg-blue-50' : 'border-gray-300'
|
||||
className={`flex justify-center rounded-[6px] border-2 border-dashed p-8 transition-colors ${
|
||||
isDragging
|
||||
? 'border-ring bg-accent/40'
|
||||
: 'border-border hover:border-ring/50'
|
||||
}`}
|
||||
onDragOver={handleDragOver}
|
||||
onDragEnter={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
onClick={triggerFileInput}
|
||||
style={{
|
||||
borderColor: isDragging
|
||||
? cssVarV2('button/primary')
|
||||
: cssVarV2('layer/insideBorder/blackBorder'),
|
||||
}}
|
||||
>
|
||||
<div className="text-center">
|
||||
<UploadIcon
|
||||
fontSize={24}
|
||||
className="mx-auto mb-3"
|
||||
style={{
|
||||
color: cssVarV2('selfhost/icon/secondary'),
|
||||
}}
|
||||
className="mx-auto mb-3 text-muted-foreground"
|
||||
/>
|
||||
<div
|
||||
className="text-xs font-medium"
|
||||
style={{ color: cssVarV2('text/secondary') }}
|
||||
>
|
||||
<div className="text-xs font-medium text-muted-foreground">
|
||||
{isDragging
|
||||
? 'Release mouse to upload file'
|
||||
: 'Upload your CSV file or drag it here'}
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-gray-500">
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{isDragging ? 'Preparing to upload...' : ''}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
+2
-3
@@ -1,4 +1,3 @@
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import type { FC, RefObject } from 'react';
|
||||
|
||||
import type { ParsedUser } from '../../utils/csv-utils';
|
||||
@@ -21,7 +20,7 @@ export const ImportPreviewContent: FC<ImportPreviewContentProps> = ({
|
||||
return (
|
||||
<div className="grid gap-3">
|
||||
{!isImported && (
|
||||
<p style={{ color: cssVarV2('text/secondary') }}>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{parsedUsers.length} users detected from the CSV file. Please confirm
|
||||
the user list below and import.
|
||||
</p>
|
||||
@@ -50,7 +49,7 @@ export const ImportInitialContent: FC<ImportInitialContentProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<div className="grid gap-3">
|
||||
<p style={{ color: cssVarV2('text/secondary') }}>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
You need to import the accounts by importing a CSV file in the correct
|
||||
format. Please download the CSV template.
|
||||
</p>
|
||||
|
||||
@@ -9,7 +9,7 @@ export const Logo = () => {
|
||||
>
|
||||
<path
|
||||
d="M18.6172 16.2657C18.314 15.7224 17.8091 14.8204 17.3102 13.9295C17.1589 13.6591 17.0086 13.3904 16.8644 13.1326C16.5679 12.6025 16.2978 12.1191 16.1052 11.7741C14.7688 9.38998 12.1376 4.66958 10.823 2.33541C10.418 1.68481 9.47943 1.73636 9.13092 2.4101C8.73553 3.1175 8.3004 3.89538 7.84081 4.71744C7.69509 4.97831 7.54631 5.24392 7.396 5.51268C5.48122 8.93556 3.24035 12.9423 1.64403 15.7961C1.5625 15.9486 1.41067 16.1974 1.33475 16.362C1.20176 16.6591 1.22775 17.0294 1.39538 17.304C1.58441 17.629 1.93802 17.8073 2.29927 17.7889C2.73389 17.7889 3.65561 17.7884 4.84738 17.7889C5.13016 17.7889 5.42823 17.7889 5.73853 17.7889C9.88246 17.7889 16.2127 17.7915 17.7663 17.7889C18.5209 17.7905 18.9942 16.9363 18.6182 16.2652L18.6172 16.2657ZM9.69699 13.2342L8.93424 11.8704C8.80024 11.6305 8.96787 11.3307 9.23588 11.3307H10.7614C11.0299 11.3307 11.1975 11.6305 11.063 11.8704L10.3003 13.2342C10.1663 13.474 9.83099 13.474 9.69648 13.2342H9.69699ZM8.41912 10.6943C8.35594 10.5281 8.30142 10.3593 8.25658 10.1878L10.7802 10.6943H8.41912ZM9.57165 14.2824C9.46414 14.4223 9.3495 14.5553 9.22823 14.6816L8.39109 12.1723L9.57114 14.2824H9.57165ZM12.0061 11.458C12.1768 11.4843 12.346 11.5206 12.5121 11.5658L10.8256 13.5687L12.0061 11.458ZM8.10117 9.33318C8.07417 9.07967 8.06245 8.82353 8.06347 8.56687L11.3962 10.2452L8.10067 9.33371L8.10117 9.33318ZM7.70579 11.8456L8.58828 15.2459C8.38905 15.3969 8.18015 15.5357 7.96411 15.663L7.70528 11.8456H7.70579ZM13.3069 11.8546C13.5332 11.9571 13.7538 12.075 13.9688 12.2043L10.8944 14.345L13.3069 11.8546ZM8.1399 7.48447C8.20104 7.01847 8.2953 6.55932 8.40943 6.1191L13.4725 10.6623L8.14041 7.48447H8.1399ZM7.01793 16.1369C6.59656 16.3152 6.16449 16.4603 5.73802 16.5781L7.01793 9.78129V16.1369ZM14.8386 12.8134C15.1988 13.1011 15.5371 13.4151 15.8494 13.737L9.50643 15.9912L14.8386 12.8134ZM10.2203 3.56456C11.1537 5.23655 12.509 7.66118 13.8002 9.96905L8.97959 4.99304C9.26288 4.48707 9.5314 4.00688 9.77902 3.56351C9.87736 3.38837 10.1219 3.38837 10.2203 3.56351V3.56456ZM2.69109 16.2358C2.95655 15.7629 3.32137 15.1144 3.40238 14.9651C4.17074 13.5913 5.20557 11.7415 6.27454 9.8302L4.50906 16.6307C3.87674 16.6307 3.33156 16.6307 2.91171 16.6307C2.71555 16.6307 2.59275 16.4114 2.69109 16.2363V16.2358ZM17.0871 16.6318C15.6151 16.6318 12.7572 16.6318 9.91965 16.6318L16.5083 14.8094C16.8537 15.4268 17.1304 15.9212 17.3077 16.2379C17.406 16.413 17.2832 16.6318 17.0876 16.6318H17.0871Z"
|
||||
fill="black"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Input } from '@affine/admin/components/ui/input';
|
||||
import { Label } from '@affine/admin/components/ui/label';
|
||||
import { Separator } from '@affine/admin/components/ui/separator';
|
||||
import type { FeatureType } from '@affine/graphql';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { ChevronRightIcon } from 'lucide-react';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
@@ -114,15 +113,15 @@ function UserForm({
|
||||
}, [defaultUser]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full gap-1">
|
||||
<div className="flex h-full flex-col bg-background">
|
||||
<RightPanelHeader
|
||||
title={title}
|
||||
handleClose={handleClose}
|
||||
handleConfirm={handleConfirm}
|
||||
canSave={canSave}
|
||||
/>
|
||||
<div className="p-4 flex-grow overflow-y-auto space-y-[8px]">
|
||||
<div className="flex flex-col rounded-md border">
|
||||
<div className="flex-grow space-y-3 overflow-y-auto p-4">
|
||||
<div className="flex flex-col rounded-xl border border-border bg-card shadow-sm">
|
||||
<InputItem
|
||||
label="User name"
|
||||
field="name"
|
||||
@@ -154,7 +153,7 @@ function UserForm({
|
||||
</div>
|
||||
|
||||
<FeatureToggleList
|
||||
className="border rounded-md"
|
||||
className="rounded-xl border border-border bg-card shadow-sm"
|
||||
features={serverConfig.availableUserFeatures}
|
||||
selected={changes.features ?? []}
|
||||
onChange={handleFeaturesChange}
|
||||
@@ -191,24 +190,18 @@ function InputItem({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1.5 p-3">
|
||||
<Label
|
||||
className="text-[15px] font-medium flex-wrap flex"
|
||||
style={{ lineHeight: '1.6rem' }}
|
||||
>
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Label className="flex flex-wrap text-xs font-medium leading-5 text-muted-foreground uppercase tracking-wide">
|
||||
{label}
|
||||
{optional && (
|
||||
<span
|
||||
className="font-normal ml-1"
|
||||
style={{ color: cssVarV2('text/secondary') }}
|
||||
>
|
||||
<span className="ml-1 font-normal text-muted-foreground">
|
||||
(optional)
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
<Input
|
||||
type="text"
|
||||
className="py-2 px-3 text-[15px] font-normal h-9"
|
||||
className="py-2 px-3 text-sm font-normal h-9"
|
||||
value={value}
|
||||
onChange={onValueChange}
|
||||
placeholder={placeholder}
|
||||
@@ -316,24 +309,24 @@ export function UpdateUserForm({
|
||||
onValidate={validateUpdateUser}
|
||||
onDirtyChange={onDirtyChange}
|
||||
actions={
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<Button
|
||||
className="w-full flex items-center justify-between text-sm font-medium px-4 py-3"
|
||||
className="h-10 w-full justify-between rounded-xl border-border/60 px-4 text-sm font-medium hover:bg-muted/50"
|
||||
variant="outline"
|
||||
onClick={onResetPassword}
|
||||
>
|
||||
<span>Reset Password</span>
|
||||
<ChevronRightIcon size={16} />
|
||||
<ChevronRightIcon size={16} className="text-muted-foreground" />
|
||||
</Button>
|
||||
<Button
|
||||
className="w-full text-red-500 px-4 py-3 rounded-md flex items-center justify-between text-sm font-medium hover:text-red-500"
|
||||
className="h-10 w-full justify-between rounded-xl border-destructive/30 px-4 text-sm font-medium text-destructive hover:bg-destructive/5 hover:text-destructive"
|
||||
variant="outline"
|
||||
onClick={onDeleteAccount}
|
||||
>
|
||||
<span>Delete Account</span>
|
||||
<ChevronRightIcon size={16} />
|
||||
</Button>
|
||||
</>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -9,34 +9,34 @@ interface UserTableProps {
|
||||
*/
|
||||
export const UserTable: React.FC<UserTableProps> = ({ users }) => {
|
||||
return (
|
||||
<div className="max-h-[300px] overflow-y-auto border rounded-md">
|
||||
<div className="max-h-[300px] overflow-y-auto rounded-xl border border-border/60 bg-card shadow-sm">
|
||||
<table className="w-full border-collapse">
|
||||
<thead className="bg-white sticky top-0">
|
||||
<thead className="sticky top-0 bg-muted/40">
|
||||
<tr>
|
||||
<th className="py-2 px-4 border-b text-left text-xs font-medium text-gray-500 tracking-wider ">
|
||||
<th className="border-b border-border px-4 py-2 text-left text-xs font-medium tracking-wider text-muted-foreground">
|
||||
Name
|
||||
</th>
|
||||
<th className="py-2 px-4 border-b text-left text-xs font-medium text-gray-500 tracking-wider">
|
||||
<th className="border-b border-border px-4 py-2 text-left text-xs font-medium tracking-wider text-muted-foreground">
|
||||
Email
|
||||
</th>
|
||||
<th className="py-2 px-4 border-b text-left text-xs font-medium text-gray-500 tracking-wider">
|
||||
<th className="border-b border-border px-4 py-2 text-left text-xs font-medium tracking-wider text-muted-foreground">
|
||||
Password
|
||||
</th>
|
||||
<th className="py-2 px-4 border-b text-left text-xs font-medium text-gray-500 tracking-wider">
|
||||
<th className="border-b border-border px-4 py-2 text-left text-xs font-medium tracking-wider text-muted-foreground">
|
||||
Status
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tbody className="divide-y divide-border">
|
||||
{users.map((user, index) => (
|
||||
<tr
|
||||
key={`${user.email}-${index}`}
|
||||
className={`${user.valid === false ? 'bg-red-50' : ''}
|
||||
${user.importStatus === ImportStatus.Failed ? 'bg-red-50' : ''}
|
||||
${user.importStatus === ImportStatus.Success ? 'bg-green-50' : ''}
|
||||
${user.importStatus === ImportStatus.Processing ? 'bg-yellow-50' : ''}`}
|
||||
className={`${user.valid === false ? 'bg-destructive/10' : ''}
|
||||
${user.importStatus === ImportStatus.Failed ? 'bg-destructive/10' : ''}
|
||||
${user.importStatus === ImportStatus.Success ? 'bg-[var(--affine-v2-layer-background-success)]' : ''}
|
||||
${user.importStatus === ImportStatus.Processing ? 'bg-[var(--affine-v2-layer-background-warning)]' : ''}`}
|
||||
>
|
||||
<td className="py-2 px-4 text-sm text-gray-900 truncate max-w-[150px]">
|
||||
<td className="max-w-[150px] truncate px-4 py-2 text-sm text-foreground">
|
||||
{user.name || '-'}
|
||||
</td>
|
||||
<td
|
||||
@@ -44,8 +44,8 @@ export const UserTable: React.FC<UserTableProps> = ({ users }) => {
|
||||
user.valid === false &&
|
||||
(user.error?.toLowerCase().includes('email') ||
|
||||
!user.error?.toLowerCase().includes('password'))
|
||||
? 'text-red-500'
|
||||
: 'text-gray-900'
|
||||
? 'text-destructive'
|
||||
: 'text-foreground'
|
||||
}`}
|
||||
>
|
||||
{user.email}
|
||||
@@ -54,36 +54,36 @@ export const UserTable: React.FC<UserTableProps> = ({ users }) => {
|
||||
className={`py-2 px-4 text-sm truncate max-w-[150px] ${
|
||||
user.valid === false &&
|
||||
user.error?.toLowerCase().includes('password')
|
||||
? 'text-red-500'
|
||||
: 'text-gray-900'
|
||||
? 'text-destructive'
|
||||
: 'text-foreground'
|
||||
}`}
|
||||
>
|
||||
{user.password || '-'}
|
||||
</td>
|
||||
<td className="py-2 px-4 text-sm">
|
||||
{user.importStatus === ImportStatus.Success ? (
|
||||
<span className="text-gray-900">
|
||||
<span className="h-2 w-2 bg-gray-900 rounded-full inline-block mr-2" />
|
||||
<span className="text-foreground">
|
||||
<span className="mr-2 inline-block h-2 w-2 rounded-full bg-[var(--affine-v2-status-success)]" />
|
||||
Success
|
||||
</span>
|
||||
) : user.importStatus === ImportStatus.Failed ? (
|
||||
<span className="text-red-500" title={user.importError}>
|
||||
<span className="h-2 w-2 bg-red-500 rounded-full inline-block mr-2" />
|
||||
<span className="text-destructive" title={user.importError}>
|
||||
<span className="mr-2 inline-block h-2 w-2 rounded-full bg-destructive" />
|
||||
Failed ({user.importError})
|
||||
</span>
|
||||
) : user.importStatus === ImportStatus.Processing ? (
|
||||
<span className="text-yellow-500">
|
||||
<span className="h-2 w-2 bg-yellow-500 rounded-full inline-block mr-2" />
|
||||
<span className="text-primary">
|
||||
<span className="mr-2 inline-block h-2 w-2 rounded-full bg-primary" />
|
||||
Processing...
|
||||
</span>
|
||||
) : user.valid === false ? (
|
||||
<span className="text-red-500" title={user.error}>
|
||||
<span className="h-2 w-2 bg-red-500 rounded-full inline-block mr-2" />
|
||||
<span className="text-destructive" title={user.error}>
|
||||
<span className="mr-2 inline-block h-2 w-2 rounded-full bg-destructive" />
|
||||
Invalid ({user.error})
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-gray-900">
|
||||
<span className="h-2 w-2 bg-gray-900 rounded-full inline-block mr-2" />
|
||||
<span className="text-foreground">
|
||||
<span className="mr-2 inline-block h-2 w-2 rounded-full bg-foreground" />
|
||||
Valid
|
||||
</span>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user