tachybase_todo/packages/plugins/@nocobase/plugin-acl/src/client/RolesMenu.tsx
2024-03-21 21:09:24 +08:00

189 lines
5.1 KiB
TypeScript

import {
ActionContextProvider,
RecordProvider,
SchemaComponent,
useAPIClient,
useResourceActionContext,
} from '@nocobase/client';
import { Menu, Empty, Dropdown, App, Tag, Row, Col, Spin } from 'antd';
import { TagOutlined, MoreOutlined } from '@ant-design/icons';
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { useACLTranslation } from './locale';
import { Schema } from '@formily/react';
import { RolesManagerContext } from './RolesManagerProvider';
import { roleEditSchema } from './schemas/roles';
import { useLoadMoreObserver } from './hooks/load-more-observer';
export const RolesMenu: React.FC & {
Item: React.FC<{ item: any; onEdit: () => void }>;
} = () => {
const { t } = useACLTranslation();
const { data, mutate } = useResourceActionContext();
const [visible, setVisible] = useState(false);
const [record, setRecord] = useState(null);
const { role, setRole } = useContext(RolesManagerContext);
const [roles, setRoles] = useState([]);
const [loading, setLoading] = useState(false);
const api = useAPIClient();
const loadMore = useCallback(async () => {
const meta = data?.meta;
if (!meta || meta.page >= meta.totalPage) {
return;
}
setLoading(true);
const res = await api.resource('roles').list({
page: meta.page + 1,
pageSize: meta.pageSize,
filter: {
'name.$ne': 'root',
},
showAnonymous: true,
sort: ['createdAt'],
appends: [],
});
mutate(res?.data || {});
setLoading(false);
}, [data]);
const { lastItem, setLastItem } = useLoadMoreObserver({ loadMore });
const handleSelect = ({ key }) => {
setRole(roles.find((item: any) => item.name === key));
};
useEffect(() => {
if (!roles[0]) {
return;
}
setRole(roles[0]);
}, [roles, setRole]);
useEffect(() => {
if (!data?.data?.length) {
return;
}
const ref = React.createRef<any>();
setLastItem(ref);
if (data.meta?.page > 1) {
setRoles((prev) => prev.concat(data.data));
} else {
setRoles(data.data);
}
}, [data, setLastItem]);
const items = useMemo(
() =>
roles.map((item: any, index: number) => ({
key: item.name,
label:
index === roles.length - 1 ? (
<div ref={lastItem}>
<RolesMenu.Item
item={item}
onEdit={() => {
setVisible(true);
setRecord(item);
}}
/>
</div>
) : (
<RolesMenu.Item
item={item}
onEdit={() => {
setVisible(true);
setRecord(item);
}}
/>
),
})),
[roles, lastItem],
);
return (
<>
{roles.length ? (
<Menu
style={{ border: 'none', maxHeight: '65vh', overflowY: 'auto' }}
items={[...items, ...(loading ? [{ key: 'loading', label: <Spin /> }] : [])]}
selectedKeys={[role?.name]}
onSelect={handleSelect}
/>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
<ActionContextProvider value={{ visible, setVisible }}>
<RecordProvider record={record} collectionName="departments">
<SchemaComponent scope={{ t }} schema={roleEditSchema} />
</RecordProvider>
</ActionContextProvider>
</>
);
};
RolesMenu.Item = function DepartmentTreeItem({ item, onEdit }) {
const { t } = useACLTranslation();
const { refreshAsync } = useResourceActionContext();
const { modal, message } = App.useApp();
const api = useAPIClient();
const deleteDepartment = () => {
modal.confirm({
title: t('Delete'),
content: t('Are you sure you want to delete it?'),
onOk: async () => {
await api.resource('roles').destroy({ filterByTk: item.name });
message.success(t('Deleted successfully'));
await refreshAsync();
},
});
};
const handleClick = ({ key, domEvent }) => {
domEvent.stopPropagation();
switch (key) {
case 'edit':
onEdit();
break;
case 'delete':
deleteDepartment();
}
};
const title = Schema.compile(item.title, { t });
return (
<Row>
<Col flex={3} style={{ display: 'inline-flex', alignItems: 'center' }}>
<span style={{ whiteSpace: 'nowrap', width: '120px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
<TagOutlined />
<span style={{ marginLeft: '10px' }} title={title}>
{title}
</span>
</span>
</Col>
<Col>
{item.default ? (
<Tag color="success" bordered={false}>
{t('Default')}
</Tag>
) : null}
<Dropdown
menu={{
items: [
{
label: t('Edit'),
key: 'edit',
},
{
label: t('Delete'),
key: 'delete',
},
],
onClick: handleClick,
}}
>
<MoreOutlined />
</Dropdown>
</Col>
</Row>
);
};