antd/lib/table/interface#ColumnsType TypeScript Examples
The following examples show how to use
antd/lib/table/interface#ColumnsType.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: submission.values.tsx From ui with GNU Affero General Public License v3.0 | 5 votes |
SubmissionValues: React.FC<Props> = (props) => {
const { t } = useTranslation()
const columns: ColumnsType<SubmissionFieldFragment> = [
{
title: t('submission:field'),
render(_, row) {
if (row.field) {
return `${row.field.title}${row.field.required ? '*' : ''}`
}
return `${row.id}`
},
},
{
title: t('submission:value'),
render(_, row) {
try {
return fieldTypes[row.type]?.displayValue(row.value)
} catch (e) {
return row.value
}
},
},
]
return (
<div>
<Descriptions title={t('submission:submission')}>
<Descriptions.Item label={t('submission:country')}>
{props.submission.geoLocation.country}
</Descriptions.Item>
<Descriptions.Item label={t('submission:city')}>
{props.submission.geoLocation.city}
</Descriptions.Item>
<Descriptions.Item label={t('submission:device.type')}>
{props.submission.device.type}
</Descriptions.Item>
<Descriptions.Item label={t('submission:device.name')}>
{props.submission.device.name}
</Descriptions.Item>
</Descriptions>
<Table columns={columns} dataSource={props.submission.fields} rowKey={'id'} />
</div>
)
}
Example #2
Source File: index.tsx From Aragorn with MIT License | 4 votes |
Dashboard = () => {
const {
state: {
uploaderProfiles,
configuration: { defaultUploaderProfileId },
uploadedFiles
}
} = useAppContext();
const history = useHistory();
const [selectRowKeys, setRowKeys] = useState([]);
const [selectRows, setSelectRows] = useState([] as UploadedFileInfo[]);
const handleProfileAdd = () => {
history.push('/uploader');
};
const handleProfileClick = id => {
if (id === defaultUploaderProfileId) {
history.push(`/profile/${id}`);
} else {
ipcRenderer.send('set-default-uploader-profile', id);
}
};
const handleCopy = url => {
clipboard.writeText(url);
message.success('已复制到粘贴板');
};
const handleOpen = path => {
shell.showItemInFolder(path);
};
const handleTableRowChange = (selectedRowKeys, selectedRows) => {
setRowKeys(selectedRowKeys);
setSelectRows(selectedRows);
};
const handleClear = () => {
const ids = selectRows.map(item => item.id);
ipcRenderer.send('clear-upload-history', ids);
setRowKeys([]);
};
const handleReUpload = () => {
const data = selectRows.map(item => {
return { id: item.uploaderProfileId, path: item.path };
});
ipcRenderer.send('file-reupload', data);
setRowKeys([]);
};
const columns: ColumnsType<UploadedFileInfo> = [
{
title: '文件名',
dataIndex: 'name',
ellipsis: true,
render: (val, record) => (
<Popover
placement="topLeft"
content={() =>
/(jpg|png|gif|jpeg)$/.test(val) ? (
<Image
style={{ maxWidth: 500 }}
src={record.url}
fallback="data:image/png;base64,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"
/>
) : (
val
)
}
trigger="hover"
>
<span onClick={() => handleCopy(record.url)} className="row-item">
{val}
</span>
</Popover>
)
},
{
title: '类型',
dataIndex: 'type',
ellipsis: true,
width: 120
},
{
title: '上传器配置',
dataIndex: 'uploaderProfileId',
ellipsis: true,
width: 120,
render: val => (
<a onClick={() => handleProfileClick(val)}>
{uploaderProfiles.find(item => item.id === val)?.name || '未找到'}
</a>
)
},
{
title: '状态',
dataIndex: 'url',
ellipsis: true,
width: 80,
render: val => (
<>
<Badge status={val ? 'success' : 'error'} />
{val ? '成功' : '失败'}
</>
)
},
{
title: '上传时间',
dataIndex: 'date',
width: 180,
ellipsis: true,
render: val => dayjs(val).format('YYYY-MM-DD HH:mm:ss')
},
{
title: '操作',
width: 80,
render: (_, record) => (
<Space>
<FolderOpenOutlined onClick={() => handleOpen(record.path)} />
<CopyOutlined onClick={() => handleCopy(record.url)} />
</Space>
)
}
];
return (
<div className="dashboard-page">
<header>
<span>控制台</span>
<Divider />
</header>
<main>
<div className="profile-wrapper">
<div className="title">上传器配置</div>
<div className="card-wrapper">
{uploaderProfiles.map(item => (
<div
key={item.id}
className={item.id === defaultUploaderProfileId ? 'card card-active' : 'card'}
onClick={() => handleProfileClick(item.id)}
>
<Box className="card-icon" />
<span>{item.name}</span>
</div>
))}
<div className="card" onClick={handleProfileAdd}>
<Plus className="card-icon" />
</div>
</div>
</div>
<div className="history-wrapper">
<div className="title">最近上传</div>
<div className="card-wrapper">
{selectRowKeys.length > 0 && (
<Space style={{ marginBottom: 10 }}>
<Button icon={<DeleteOutlined />} onClick={handleClear}>
清除
</Button>
<Button icon={<UploadOutlined />} onClick={handleReUpload}>
重新上传
</Button>
</Space>
)}
<Table
size="small"
rowKey="id"
dataSource={uploadedFiles}
columns={columns}
rowSelection={{ onChange: handleTableRowChange, selectedRowKeys: selectRowKeys }}
/>
</div>
</div>
</main>
</div>
);
}
Example #3
Source File: index.tsx From Aragorn with MIT License | 4 votes |
FileManage = () => {
const {
state: {
uploaderProfiles,
configuration: { defaultUploaderProfileId }
}
} = useAppContext();
const [windowHeight, setWindowHeight] = useState(window.innerHeight);
useEffect(() => {
function handleResize() {
setWindowHeight(window.innerHeight);
}
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
const { id } = useParams<{ id: string }>();
const [hasFileManageFeature, setHasFileManageFeature] = useState(false);
const [uploaderProfile, setUploaderProfile] = useState({} as UploaderProfile);
useEffect(() => {
const currentId = id || defaultUploaderProfileId;
setCurrentProfile(currentId as string);
}, []);
useEffect(() => {
if (uploaderProfile?.id) {
getList();
}
}, [uploaderProfile]);
const [list, setList] = useState([] as ListFile[]);
const [listLoading, setListLoading] = useState(false);
const getList = (directoryPath?: string) => {
setListLoading(true);
ipcRenderer.send('file-list-get', uploaderProfile.id, directoryPath);
};
const [dirPath, setDirPath] = useState([] as string[]);
useEffect(() => {
function handleListGetReply(_, res?: FileListResponse) {
setListLoading(false);
if (res === undefined) {
setHasFileManageFeature(false);
setList([]);
message.info(`${uploaderProfile.uploaderName}暂不支持文件管理功能`);
return;
}
setHasFileManageFeature(true);
if (res.success) {
setList(res.data);
} else {
message.error(`文件列表获取失败 ${res.desc || ''}`);
}
}
function handleFileDeleteReply(_, res?: DeleteFileResponse) {
if (res === undefined) {
return;
}
if (res.success) {
message.success({ content: '文件删除成功', key: 'file-manage-delete' });
getList(dirPath.join('/'));
} else {
message.error({ content: `文件删除失败 ${res.desc || ''}`, key: 'file-manage-delete' });
}
}
function handleFileUploadReply() {
getList(dirPath.join('/'));
}
function handleDirectoryCreateReply(_, res?: CreateDirectoryResponse) {
if (res === undefined) {
return;
}
if (res.success) {
message.success('目录创建成功');
setModalVisible(false);
getList(dirPath.join('/'));
} else {
message.error(`目录创建失败 ${res.desc || ''}`);
}
}
function handleExportReplay(_, res) {
setExportLoading(false);
if (res) {
shell.showItemInFolder(res);
setRowKeys([]);
setSelectRows([]);
}
}
ipcRenderer.on('file-list-get-reply', handleListGetReply);
ipcRenderer.on('file-delete-reply', handleFileDeleteReply);
ipcRenderer.on('file-upload-reply', handleFileUploadReply);
ipcRenderer.on('directory-create-reply', handleDirectoryCreateReply);
ipcRenderer.on('export-reply', handleExportReplay);
return () => {
ipcRenderer.removeListener('file-list-get-reply', handleListGetReply);
ipcRenderer.removeListener('file-delete-reply', handleFileDeleteReply);
ipcRenderer.removeListener('file-upload-reply', handleFileUploadReply);
ipcRenderer.removeListener('directory-create-reply', handleDirectoryCreateReply);
ipcRenderer.removeListener('export-reply', handleExportReplay);
};
}, [uploaderProfile, dirPath]);
const handleNameClick = (record: ListFile) => {
if (record.type === 'directory') {
const newPath = [...dirPath, formatFileName(record.name)];
setDirPath(newPath);
getList(newPath.join('/'));
} else {
clipboard.writeText(record.url as string);
message.success('链接已复制到粘贴板');
}
};
const handlePathClick = (index: number) => {
if (index === -1) {
setDirPath([]);
getList();
} else {
const newPath = dirPath.slice(0, index + 1);
setDirPath(newPath);
getList(newPath.join('/'));
}
};
const setCurrentProfile = (uploaderProfileId: string) => {
setDirPath([]);
const uploaderProfile = uploaderProfiles.find(item => item.id === uploaderProfileId);
setUploaderProfile(uploaderProfile as UploaderProfile);
};
const formatFileName = (name: string) => {
if (dirPath.length > 0) {
const pathPrefix = dirPath.join('/') + '/';
return name.split(pathPrefix).pop() || '';
} else {
return name;
}
};
const [selectRowKeys, setRowKeys] = useState([] as string[]);
const [selectRows, setSelectRows] = useState([] as ListFile[]);
const handleTableRowChange = (selectedRowKeys, selectedRows: ListFile[]) => {
setRowKeys(selectedRowKeys);
setSelectRows(selectedRows);
};
const handleRefresh = () => {
getList(dirPath.join('/'));
};
const handleBatchDelete = () => {
Modal.confirm({
title: '确认删除',
onOk: () => {
const names = selectRows.map(item => [...dirPath, formatFileName(item.name)].join('/'));
message.info({ content: '正在删除,请稍后...', key: 'file-manage-delete' });
ipcRenderer.send('file-delete', uploaderProfile.id, names);
}
});
};
const handleDelete = (record: ListFile) => {
let name = record.name;
Modal.confirm({
title: '确认删除',
content: name,
onOk: () => {
let name = record.name;
if (record.type === 'directory') {
name = `${[...dirPath, record.name].join('/')}/`;
} else {
name = [...dirPath, formatFileName(record.name)].join('/');
}
message.info({ content: '正在删除,请稍后...', key: 'file-manage-delete' });
ipcRenderer.send('file-delete', uploaderProfile.id, [name]);
}
});
};
const uploadRef = useRef<HTMLInputElement>(null);
const handleFileUpload = (event: React.FormEvent<HTMLInputElement>) => {
const fileList = event.currentTarget.files || [];
const filesPath = Array.from(fileList).map(file => file.path);
const pathPrefix = dirPath.join('/');
ipcRenderer.send('file-upload', uploaderProfile.id, filesPath, pathPrefix);
event.currentTarget.value = '';
};
const [modalVisible, setModalVisible] = useState(false);
const [form] = Form.useForm();
const handleCreateDirectory = () => {
form.validateFields().then(values => {
ipcRenderer.send('directory-create', uploaderProfile.id, values?.directoryPath || '');
});
};
const handleDownload = (record: ListFile) => {
ipcRenderer.send('file-download', record.name, record.url);
};
const [exportLoading, setExportLoading] = useState(false);
const handleExport = () => {
const data = selectRows.map(item => {
const fileNameArr = item.name.split('.');
fileNameArr.pop();
return {
name: fileNameArr.join('.'),
url: item.url
};
});
setExportLoading(true);
ipcRenderer.send('export', data);
};
const columns: ColumnsType<ListFile> = [
{
title: '文件名',
dataIndex: 'name',
ellipsis: true,
render: (val: string, record: ListFile) => (
<div style={{ display: 'flex', alignItems: 'center' }}>
{record.type === 'directory' ? (
<FolderFilled style={{ fontSize: 16 }} />
) : (
<FileOutlined style={{ fontSize: 16 }} />
)}
{record.type === 'directory' ? (
<a
title={val}
onClick={() => handleNameClick(record)}
className="table-filename"
style={{ marginLeft: 10, overflow: 'hidden', textOverflow: 'ellipsis' }}
>
{formatFileName(val)}
</a>
) : (
<Popover
placement="topLeft"
content={() =>
/(jpg|png|gif|jpeg)$/.test(val) ? (
<Image
style={{ maxWidth: 500 }}
src={record.url}
fallback="data:image/png;base64,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"
/>
) : (
val
)
}
trigger="hover"
>
<a
title={val}
onClick={() => handleNameClick(record)}
className="table-filename"
style={{ marginLeft: 10, overflow: 'hidden', textOverflow: 'ellipsis' }}
>
{formatFileName(val)}
</a>
</Popover>
)}
</div>
)
},
{
title: '文件大小',
dataIndex: 'size',
ellipsis: true,
width: 120,
render: val => (val ? filesize(val) : '-')
},
{
title: '更新时间',
dataIndex: 'lastModified',
ellipsis: true,
width: 200,
render: val => (val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : '-')
},
{
title: '操作',
width: 120,
render: (_, record) => (
<Space>
{record.type !== 'directory' && (
<>
<DownloadOutlined onClick={() => handleDownload(record)} />
<CopyOutlined onClick={() => handleNameClick(record)} />
</>
)}
<DeleteOutlined onClick={() => handleDelete(record)} />
</Space>
)
}
];
return (
<div className="storage-page">
<header>
<span>文件管理</span>
<Divider />
</header>
<Space style={{ marginBottom: 10 }}>
<Select style={{ minWidth: 120 }} value={uploaderProfile?.id} onChange={setCurrentProfile}>
{uploaderProfiles.map(item => (
<Select.Option key={item.name} value={item.id}>
{item.name}
</Select.Option>
))}
</Select>
<Button
title="上传"
icon={<UploadOutlined />}
disabled={!hasFileManageFeature}
type="primary"
onClick={() => {
uploadRef.current?.click();
}}
/>
<Button title="刷新" icon={<ReloadOutlined />} disabled={!hasFileManageFeature} onClick={handleRefresh} />
<Button
title="创建文件夹"
icon={<FolderAddOutlined />}
disabled={!hasFileManageFeature}
onClick={() => {
setModalVisible(true);
}}
/>
<Button
title="导出"
icon={<ExportOutlined />}
disabled={selectRows.length === 0}
onClick={handleExport}
loading={exportLoading}
/>
<Button title="删除" icon={<DeleteOutlined />} disabled={selectRows.length === 0} onClick={handleBatchDelete} />
</Space>
<Breadcrumb style={{ marginBottom: 10 }}>
<Breadcrumb.Item>
<a onClick={() => handlePathClick(-1)}>全部文件</a>
</Breadcrumb.Item>
{dirPath.map((item, index) => (
<Breadcrumb.Item key={item}>
<a onClick={() => handlePathClick(index)}>{item}</a>
</Breadcrumb.Item>
))}
</Breadcrumb>
<div className="table-wrapper">
<Table
size="small"
rowKey="name"
scroll={{ y: windowHeight - 270 }}
dataSource={list}
columns={columns}
pagination={{
size: 'small',
defaultPageSize: 100,
pageSizeOptions: ['50', '100', '200'],
hideOnSinglePage: true
}}
loading={listLoading}
rowSelection={{
onChange: handleTableRowChange,
selectedRowKeys: selectRowKeys,
getCheckboxProps: record => ({ disabled: record?.type === 'directory' })
}}
/>
</div>
<input ref={uploadRef} type="file" multiple hidden onChange={handleFileUpload} />
<Modal
title="创建目录"
visible={modalVisible}
onCancel={() => setModalVisible(false)}
onOk={handleCreateDirectory}
destroyOnClose={true}
>
<Form form={form} preserve={false}>
<Form.Item
label="目录名称"
name="directoryPath"
rules={[{ required: true }, { pattern: domainPathRegExp, message: '目录名不能以 / 开头或结尾' }]}
>
<Input autoFocus />
</Form.Item>
</Form>
</Modal>
</div>
);
}
Example #4
Source File: submissions.tsx From ui with GNU Affero General Public License v3.0 | 4 votes |
Submissions: NextPage = () => {
const { t } = useTranslation()
const router = useRouter()
const [pagination, setPagination] = useState<PaginationProps>({
pageSize: 25,
})
const [form, setForm] = useState<FormPagerFragment>()
const [entries, setEntries] = useState<SubmissionFragment[]>()
const { loading, refetch } = useSubmissionPagerQuery({
variables: {
form: router.query.id as string,
limit: pagination.pageSize,
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0,
filter: {
excludeEmpty: true,
},
},
onCompleted: ({ pager, form }) => {
setPagination({
...pagination,
total: pager.total,
})
setForm(form)
setEntries(pager.entries)
},
})
const columns: ColumnsType<SubmissionFragment> = [
{
title: t('submission:progress'),
render(_, row) {
const props: ProgressProps = {
status: 'active',
percent: Math.round(row.percentageComplete * 100),
}
if (row.percentageComplete >= 1) {
props.status = 'success'
} else if (dayjs().diff(dayjs(row.lastModified), 'hour') > 4) {
props.status = 'exception'
}
return <Progress {...props} />
},
},
{
title: t('submission:created'),
dataIndex: 'created',
render(date: string) {
return <DateTime date={date} />
},
responsive: ['lg'],
},
{
title: t('submission:lastModified'),
dataIndex: 'lastModified',
render(date: string) {
return <TimeAgo date={date} />
},
responsive: ['lg'],
},
]
return (
<Structure
title={t('admin:submissions')}
selected={'forms'}
loading={loading}
breadcrumbs={[
{ href: '/admin', name: t('admin:home') },
{ href: '/admin/forms', name: t('admin:forms') },
{
href: '/admin/forms/[id]',
name: loading || !form ? t('form:loading') : t('form:mange', { title: form.title }),
as: `/admin/forms/${router.query.id as string}`,
},
]}
padded={false}
extra={[
<ExportSubmissionAction
key={'export'}
form={router.query.id as string}
trigger={(onClick, loading) => (
<Button onClick={onClick} loading={loading}>
{t('submission:export')}
</Button>
)}
/>,
<Link
key={'edit'}
href={'/admin/forms/[id]'}
as={`/admin/forms/${router.query.id as string}`}
>
<Button>{t('submission:edit')}</Button>
</Link>,
<Button
key={'web'}
href={`/form/${router.query.id as string}`}
target={'_blank'}
type={'primary'}
>
{t('submission:add')}
</Button>,
]}
>
<Table
columns={columns}
dataSource={entries}
rowKey={'id'}
pagination={pagination}
expandable={{
expandedRowRender(record) {
return <SubmissionValues form={form} submission={record} />
},
rowExpandable(record) {
return record.percentageComplete > 0
},
}}
onChange={async (next) => {
setPagination(next)
await refetch()
}}
/>
</Structure>
)
}
Example #5
Source File: index.tsx From ui with GNU Affero General Public License v3.0 | 4 votes |
Index: NextPage = () => {
const { t } = useTranslation()
const { width } = useWindowSize()
const [pagination, setPagination] = useState<PaginationProps>({
pageSize: 25,
})
const [entries, setEntries] = useState<FormPagerFragment[]>()
const { loading, refetch, error } = useFormPagerQuery({
variables: {
limit: pagination.pageSize,
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0,
},
onCompleted: ({ pager }) => {
setPagination({
...pagination,
total: pager.total,
})
setEntries(pager.entries)
},
})
const [executeDelete] = useFormDeleteMutation()
const deleteForm = async (id: string) => {
try {
await executeDelete({
variables: {
id,
},
})
const next = entries.filter((entry) => entry.id !== id)
if (next.length === 0) {
setPagination({ ...pagination, current: 1 })
} else {
setEntries(next)
}
await message.success(t('form:deleted'))
} catch (e) {
await message.error(t('form:deleteError'))
}
}
const columns: ColumnsType<FormPagerFragment> = [
{
title: t('form:row.isLive'),
dataIndex: 'isLive',
render(live: boolean) {
return <FormIsLive isLive={live} />
},
responsive: ['md'],
},
{
title: t('form:row.title'),
dataIndex: 'title',
},
{
title: t('form:row.admin'),
dataIndex: 'admin',
render(_, { admin: user }) {
if (!user) {
return <Tag color={'red'} title={t('form:row.adminMissing')} />
}
return (
<Tooltip title={user.email}>
<Link href={`/admin/users/${user.id}`} passHref>
<Button type={'dashed'}>{user.username}</Button>
</Link>
</Tooltip>
)
},
responsive: ['lg'],
},
{
title: t('form:row.language'),
dataIndex: 'language',
render(lang: string) {
return t(`language:${lang}`)
},
responsive: ['lg'],
},
{
title: t('form:row.created'),
dataIndex: 'created',
render(date: string) {
return <DateTime date={date} />
},
responsive: ['lg'],
},
{
title: t('form:row.lastModified'),
dataIndex: 'lastModified',
render(date: string) {
return <TimeAgo date={date} />
},
responsive: ['md'],
},
{
title: t('form:row.menu'),
align: 'right',
render(_, row) {
return (
<Space direction={width < 600 ? 'vertical' : 'horizontal'}>
<Tooltip title={'Show Submissions'}>
<Link href={`/admin/forms/${row.id}/submissions`} passHref>
<Button>
<UnorderedListOutlined />
</Button>
</Link>
</Tooltip>
<Link href={`/admin/forms/${row.id}`} passHref>
<Button type={'primary'}>
<EditOutlined />
</Button>
</Link>
<Popconfirm
title={t('form:confirmDelete')}
onConfirm={() => deleteForm(row.id)}
okText={t('form:deleteNow')}
okButtonProps={{ danger: true }}
>
<Button danger>
<DeleteOutlined />
</Button>
</Popconfirm>
{row.isLive && (
<Button href={`/form/${row.id}`} target={'_blank'}>
<GlobalOutlined />
</Button>
)}
{!row.isLive && (
<Tooltip title={'Not Public accessible!'} placement={'left'}>
<Button href={`/form/${row.id}`} target={'_blank'}>
<WarningOutlined />
</Button>
</Tooltip>
)}
</Space>
)
},
},
]
return (
<Structure
title={t('admin:forms')}
selected={'forms'}
loading={loading}
breadcrumbs={[{ href: '/admin', name: t('admin:home') }]}
padded={false}
extra={[
<Link key={'create'} href={'/admin/forms/create'}>
<Button type={'primary'}>{t('form:new')}</Button>
</Link>,
]}
error={error?.message}
>
<Table
columns={columns}
dataSource={entries}
rowKey={'id'}
pagination={pagination}
onChange={async (next) => {
setPagination(next)
await refetch()
}}
/>
</Structure>
)
}
Example #6
Source File: index.tsx From ui with GNU Affero General Public License v3.0 | 4 votes |
Index: NextPage = () => {
const { width } = useWindowSize()
const { t } = useTranslation()
const [pagination, setPagination] = useState<PaginationProps>({
pageSize: 10,
})
const [entries, setEntries] = useState<UserPagerFragment[]>()
const { loading, refetch, error } = useUserPagerQuery({
variables: {
limit: pagination.pageSize,
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0,
},
onCompleted: ({ pager }) => {
setPagination({
...pagination,
total: pager.total,
})
setEntries(pager.entries)
},
})
const [executeDelete] = useUserDeleteMutation()
const deleteUser = async (id: string) => {
try {
await executeDelete({
variables: {
id,
},
})
const next = entries.filter((entry) => entry.id !== id)
if (next.length === 0) {
setPagination({ ...pagination, current: 1 })
} else {
setEntries(next)
}
await message.success(t('user:deleted'))
} catch (e) {
await message.error(t('user:deleteError'))
}
}
const columns: ColumnsType<UserPagerFragment> = [
{
title: t('user:row.roles'),
dataIndex: 'roles',
render(roles: string[]) {
return <UserRole roles={roles} />
},
responsive: ['md'],
},
{
title: t('user:row.email'),
render(_, row) {
return <Tag color={row.verifiedEmail ? 'lime' : 'volcano'}>{row.email}</Tag>
},
},
{
title: t('user:row.created'),
dataIndex: 'created',
render(date: string) {
return <DateTime date={date} />
},
responsive: ['lg'],
},
{
title: t('user:row.menu'),
align: 'right',
render(_, row) {
return (
<Space direction={width < 600 ? 'vertical' : 'horizontal'}>
<Link href={`/admin/users/${row.id}`} passHref>
<Button type={'primary'}>
<EditOutlined />
</Button>
</Link>
<Popconfirm
title={t('user:confirmDelete')}
onConfirm={() => deleteUser(row.id)}
okText={t('user:deleteNow')}
okButtonProps={{ danger: true }}
>
<Button danger>
<DeleteOutlined />
</Button>
</Popconfirm>
</Space>
)
},
},
]
return (
<Structure
title={t('admin:users')}
loading={loading}
breadcrumbs={[{ href: '/admin', name: t('admin:home') }]}
padded={false}
error={error?.message}
>
<Table
columns={columns}
dataSource={entries}
rowKey={'id'}
pagination={pagination}
onChange={async (next) => {
setPagination(next)
await refetch()
}}
/>
</Structure>
)
}