@gadmin2n/schematics 0.0.140 → 0.0.141

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +1 -1
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +1 -1
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/gen-jwt.ts +8 -1
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +32 -4
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +39 -33
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.spec.ts +34 -1
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +26 -0
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +149 -0
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.ts +20 -6
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.spec.ts +85 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.ts +157 -0
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.spec.ts +59 -0
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +15 -4
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +125 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +109 -2
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/PageHeader.tsx +46 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +1 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.test.ts +10 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +6 -4
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/constants/layout.ts +30 -2
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +20 -1
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +20 -1
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +9 -20
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/list.tsx +141 -138
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +8 -19
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/human-approval/inbox.tsx +12 -19
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +47 -150
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/permissionReadme/index.tsx +13 -12
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +110 -113
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +61 -64
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +187 -200
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/index.tsx +46 -44
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NewNodeDefaults.test.tsx +6 -2
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.tsx +50 -41
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +9 -22
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instances.tsx +4 -8
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/index.tsx +9 -22
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.test.tsx +42 -0
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.tsx +77 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.test.tsx +29 -1
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.tsx +245 -55
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.test.tsx +152 -0
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +417 -161
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/newNodeConfig.ts +5 -1
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/index.css +20 -0
  46. package/package.json +1 -1
@@ -10,6 +10,7 @@ import { useGetIdentity } from '@refinedev/core';
10
10
  import {
11
11
  Table,
12
12
  Button,
13
+ Card,
13
14
  Space,
14
15
  Tooltip,
15
16
  Tag,
@@ -17,7 +18,6 @@ import {
17
18
  Modal,
18
19
  message,
19
20
  Tabs,
20
- Typography,
21
21
  } from 'antd';
22
22
  import {
23
23
  SaveOutlined,
@@ -59,14 +59,13 @@ import { PageDetailDrawer } from './Components/PageDetailDrawer';
59
59
  import type {
60
60
  PageMenuTreeNode,
61
61
  PageResourceAssignment,
62
- PageResourcePermissionSummary,
63
62
  ResourceDefinition,
64
63
  } from './types';
65
64
  import { usePageManagement } from './hooks/usePageManagement';
66
65
  import { useTranslation } from 'react-i18next';
67
66
  import { SqlModal, generatePageSql } from '../../components/SqlModal';
68
-
69
- const RESOURCE_NAME_DISPLAY_TRUNCATION_LIMIT = 40;
67
+ import { PageHeader } from 'components';
68
+ import { PAGE_WRAPPER_STYLE } from 'constants/layout';
70
69
 
71
70
  // Sparse number gap for sortOrder (allows inserting items between existing ones)
72
71
  const SORT_ORDER_GAP = 10000;
@@ -92,7 +91,7 @@ const safelyConvertToNumber = (value: unknown): number | null => {
92
91
 
93
92
  const extractPermissionActionsFromAssociation = (
94
93
  associationRecord: any,
95
- resourceRecord: any,
94
+ _resourceRecord: any,
96
95
  ): string[] => {
97
96
  const aggregatedPermissionActions = new Set<string>();
98
97
 
@@ -295,7 +294,8 @@ export const PageManagementListPage: React.FC = () => {
295
294
  children: removeEmptyChildrenArrays(node.children),
296
295
  };
297
296
  }
298
- const { children, ...nodeWithoutChildren } = node;
297
+ const nodeWithoutChildren = { ...node };
298
+ delete nodeWithoutChildren.children;
299
299
  return nodeWithoutChildren as PageMenuTreeNode;
300
300
  });
301
301
 
@@ -303,69 +303,6 @@ export const PageManagementListPage: React.FC = () => {
303
303
  return removeEmptyChildrenArrays(rootLevelNodes);
304
304
  }, [pageListData]);
305
305
 
306
- const pageResourcePermissionSummaryMap = useMemo<
307
- Record<number, PageResourcePermissionSummary>
308
- >(() => {
309
- const associationsArray: any[] = Array.isArray(
310
- pageResourceAssociationsData?.data,
311
- )
312
- ? pageResourceAssociationsData.data
313
- : [];
314
- const summaryMapByPageId: Record<number, PageResourcePermissionSummary> =
315
- {};
316
-
317
- associationsArray.forEach((association: any) => {
318
- const pageId = safelyConvertToNumber(association?.pageId);
319
- const resourceId = safelyConvertToNumber(association?.resourceId);
320
-
321
- if (pageId === null || resourceId === null) {
322
- return;
323
- }
324
-
325
- if (!summaryMapByPageId[pageId]) {
326
- summaryMapByPageId[pageId] = {
327
- resourceIds: [],
328
- resourceNames: [],
329
- allPermissionActions: [],
330
- };
331
- }
332
-
333
- const pageSummary = summaryMapByPageId[pageId];
334
-
335
- if (!pageSummary.resourceIds.includes(resourceId)) {
336
- pageSummary.resourceIds.push(resourceId);
337
- }
338
-
339
- const resourceRecord =
340
- association?.resource ?? resourceLookupByIdMap[resourceId];
341
- const resourceDisplayName =
342
- resourceRecord?.name ||
343
- resourceRecord?.code ||
344
- `Resource ${resourceId}`;
345
-
346
- if (
347
- resourceDisplayName &&
348
- !pageSummary.resourceNames.includes(resourceDisplayName)
349
- ) {
350
- pageSummary.resourceNames.push(resourceDisplayName);
351
- }
352
-
353
- extractPermissionActionsFromAssociation(
354
- association,
355
- resourceRecord,
356
- ).forEach((permissionAction) => {
357
- if (
358
- permissionAction &&
359
- !pageSummary.allPermissionActions.includes(permissionAction)
360
- ) {
361
- pageSummary.allPermissionActions.push(permissionAction);
362
- }
363
- });
364
- });
365
-
366
- return summaryMapByPageId;
367
- }, [pageResourceAssociationsData, resourceLookupByIdMap]);
368
-
369
306
  const getPageResourceAssignmentsForModal = useCallback(
370
307
  (pageId: number): PageResourceAssignment[] => {
371
308
  console.log(
@@ -541,7 +478,12 @@ export const PageManagementListPage: React.FC = () => {
541
478
  );
542
479
  setSqlModal({ open: true, title: `SQL — Page: ${record.name}`, sql });
543
480
  },
544
- [pageListData, pageResourceAssociationsData, resourceLookupByIdMap],
481
+ [
482
+ pageListData,
483
+ pageResourceAssociationsData,
484
+ resourceLookupByIdMap,
485
+ identity,
486
+ ],
545
487
  );
546
488
 
547
489
  // Ref for expand button click/double-click disambiguation
@@ -1001,46 +943,6 @@ export const PageManagementListPage: React.FC = () => {
1001
943
  setExpandedKeys(new Set());
1002
944
  }, []);
1003
945
 
1004
- // Common column renderers for both tables
1005
- const renderAssignedResourcesColumn = (record: any) => {
1006
- const permissionSummary = pageResourcePermissionSummaryMap[record.id];
1007
-
1008
- if (!permissionSummary?.resourceNames.length) {
1009
- return <span style={{ color: '#999' }}>No resources assigned</span>;
1010
- }
1011
-
1012
- const joinedResourceNames = permissionSummary.resourceNames.join(', ');
1013
- const shouldTruncateDisplay =
1014
- joinedResourceNames.length > RESOURCE_NAME_DISPLAY_TRUNCATION_LIMIT;
1015
- const truncatedDisplayValue = shouldTruncateDisplay
1016
- ? `${joinedResourceNames.slice(0, RESOURCE_NAME_DISPLAY_TRUNCATION_LIMIT)}...`
1017
- : joinedResourceNames;
1018
-
1019
- return (
1020
- <Tooltip title={joinedResourceNames}>
1021
- <span>{truncatedDisplayValue}</span>
1022
- </Tooltip>
1023
- );
1024
- };
1025
-
1026
- const renderPermissionActionsColumn = (record: any) => {
1027
- const permissionSummary = pageResourcePermissionSummaryMap[record.id];
1028
-
1029
- if (!permissionSummary?.allPermissionActions.length) {
1030
- return <span style={{ color: '#999' }}>-</span>;
1031
- }
1032
-
1033
- return (
1034
- <Space size={[4, 4]} wrap>
1035
- {permissionSummary.allPermissionActions.map((permissionAction) => (
1036
- <Tag key={`${record.id}-${permissionAction}`} color="blue">
1037
- {permissionAction}
1038
- </Tag>
1039
- ))}
1040
- </Space>
1041
- );
1042
- };
1043
-
1044
946
  // Handle click on add child page button
1045
947
  const handleAddChildPage = useCallback((parentPage: any) => {
1046
948
  const parentPath = parentPage.path || '';
@@ -1280,48 +1182,43 @@ export const PageManagementListPage: React.FC = () => {
1280
1182
  );
1281
1183
 
1282
1184
  return (
1283
- <div>
1284
- <div
1285
- style={{
1286
- display: 'flex',
1287
- justifyContent: 'space-between',
1288
- alignItems: 'center',
1289
- marginBottom: 16,
1290
- }}
1291
- >
1292
- <Typography.Title level={4} style={{ margin: 0 }}>
1293
- Page mgnts
1294
- </Typography.Title>
1295
- <Button
1296
- type="primary"
1297
- onClick={() =>
1298
- setActiveModalState({
1299
- modalType: 'create',
1300
- targetPageId: null,
1301
- })
1302
- }
1303
- >
1304
- Create Page
1305
- </Button>
1306
- </div>
1307
-
1308
- <Tabs
1309
- activeKey={activeTab}
1310
- onChange={setActiveTab}
1311
- items={[
1312
- {
1313
- key: 'regular',
1314
- label: `Regular Pages (${regularPagesData.length})`,
1315
- children: renderRegularPagesTable(),
1316
- },
1317
- {
1318
- key: 'virtual',
1319
- label: `Virtual Pages (${virtualPagesData.length})`,
1320
- children: renderVirtualPagesTable(),
1321
- },
1322
- ]}
1185
+ <div style={PAGE_WRAPPER_STYLE}>
1186
+ <PageHeader
1187
+ title="Page mgnts"
1188
+ extra={
1189
+ <Button
1190
+ type="primary"
1191
+ onClick={() =>
1192
+ setActiveModalState({
1193
+ modalType: 'create',
1194
+ targetPageId: null,
1195
+ })
1196
+ }
1197
+ >
1198
+ Create Page
1199
+ </Button>
1200
+ }
1323
1201
  />
1324
1202
 
1203
+ <Card className="card-table">
1204
+ <Tabs
1205
+ activeKey={activeTab}
1206
+ onChange={setActiveTab}
1207
+ items={[
1208
+ {
1209
+ key: 'regular',
1210
+ label: `Regular Pages (${regularPagesData.length})`,
1211
+ children: renderRegularPagesTable(),
1212
+ },
1213
+ {
1214
+ key: 'virtual',
1215
+ label: `Virtual Pages (${virtualPagesData.length})`,
1216
+ children: renderVirtualPagesTable(),
1217
+ },
1218
+ ]}
1219
+ />
1220
+ </Card>
1221
+
1325
1222
  {activeModalState.modalType === 'edit' &&
1326
1223
  activeModalState.targetPageId && (
1327
1224
  <EditPageModal
@@ -1,4 +1,6 @@
1
1
  import { Alert, Card, Collapse, Divider, Table, Tabs, Typography } from 'antd';
2
+ import { PageHeader } from 'components';
3
+ import { CARD_GAP_STYLE, PAGE_WRAPPER_STYLE } from 'constants/layout';
2
4
 
3
5
  const { Title, Paragraph, Text } = Typography;
4
6
 
@@ -333,16 +335,14 @@ const comparisonData = [
333
335
 
334
336
  export const PermissionReadmePage = () => {
335
337
  return (
336
- <div>
337
- <Typography>
338
- <Title level={2}>菜单管理说明</Title>
339
- <Paragraph>
340
- 本页面介绍系统的菜单、路由与权限管理机制,帮助管理员理解如何添加和配置不同类型的菜单项。
341
- </Paragraph>
342
- </Typography>
338
+ <div style={PAGE_WRAPPER_STYLE}>
339
+ <PageHeader
340
+ title="菜单管理说明"
341
+ subtitle="本页面介绍系统的菜单、路由与权限管理机制,帮助管理员理解如何添加和配置不同类型的菜单项。"
342
+ />
343
343
 
344
344
  {/* ── 架构概览 ──────────────────────────────────────────────── */}
345
- <Card title="菜单系统架构" style={{ marginBottom: 24 }}>
345
+ <Card title="菜单系统架构" style={CARD_GAP_STYLE}>
346
346
  <Paragraph>
347
347
  系统的菜单管理涉及<Text strong>前端路由注册</Text>和
348
348
  <Text strong>数据库页面配置</Text>两个部分的协作。 未匹配到
@@ -374,7 +374,7 @@ export const PermissionReadmePage = () => {
374
374
  </Card>
375
375
 
376
376
  {/* ── 核心概念 ──────────────────────────────────────────────── */}
377
- <Card title="核心概念" style={{ marginBottom: 24 }}>
377
+ <Card title="核心概念" style={CARD_GAP_STYLE}>
378
378
  <Table
379
379
  columns={coreConceptColumns}
380
380
  dataSource={coreConceptData}
@@ -394,7 +394,7 @@ export const PermissionReadmePage = () => {
394
394
  </Card>
395
395
 
396
396
  {/* ── 四种注册方式 ──────────────────────────────────────────── */}
397
- <Card title="菜单注册方式" style={{ marginBottom: 24 }}>
397
+ <Card title="菜单注册方式" style={CARD_GAP_STYLE}>
398
398
  <Alert
399
399
  type="info"
400
400
  showIcon
@@ -755,7 +755,8 @@ staff: {
755
755
  目标网站必须允许被 iframe 嵌入(未设置{' '}
756
756
  <Text code>X-Frame-Options: DENY</Text> 或{' '}
757
757
  <Text code>
758
- Content-Security-Policy: frame-ancestors 'none'
758
+ Content-Security-Policy: frame-ancestors
759
+ &apos;none&apos;
759
760
  </Text>
760
761
  )
761
762
  </li>
@@ -876,7 +877,7 @@ interface DynamicMenuGroup {
876
877
  </Card>
877
878
 
878
879
  {/* ── 权限层级关系 ────────────────────────────────────────────── */}
879
- <Card title="权限层级关系" style={{ marginBottom: 24 }}>
880
+ <Card title="权限层级关系" style={CARD_GAP_STYLE}>
880
881
  <Title level={5}>实体关系总览</Title>
881
882
  <pre
882
883
  style={{
@@ -1,6 +1,6 @@
1
1
  import React, { useState, useMemo } from 'react';
2
2
  import { useTable, FilterDropdown } from '@refinedev/antd';
3
- import { Table, Button, Space, Tooltip, Input, Typography } from 'antd';
3
+ import { Table, Button, Card, Space, Tooltip, Input } from 'antd';
4
4
  import {
5
5
  EditOutlined,
6
6
  EyeOutlined,
@@ -17,6 +17,8 @@ import { EditModal } from './Components/EditModal';
17
17
  import { CreateModal } from './Components/CreateModal';
18
18
  import { ResourceDetailDrawer } from './Components/ResourceDetailDrawer';
19
19
  import { SqlModal, generateResourceSql } from '../../components/SqlModal';
20
+ import { PageHeader } from 'components';
21
+ import { PAGE_WRAPPER_STYLE } from 'constants/layout';
20
22
 
21
23
  // System resources that cannot be modified (use Set for O(1) lookup performance)
22
24
  // System resources that cannot be modified
@@ -116,119 +118,114 @@ export const ResourceManagementListPage: React.FC = () => {
116
118
  };
117
119
 
118
120
  return (
119
- <div>
120
- <div
121
- style={{
122
- display: 'flex',
123
- justifyContent: 'space-between',
124
- alignItems: 'center',
125
- marginBottom: 16,
126
- }}
127
- >
128
- <Typography.Title level={4} style={{ margin: 0 }}>
129
- Resource mgnts
130
- </Typography.Title>
131
- <Button
132
- type="primary"
133
- onClick={() =>
134
- setShowModal({
135
- type: 'create',
136
- id: null,
137
- })
138
- }
139
- >
140
- Create Resource
141
- </Button>
142
- </div>
143
- <Table {...tableProps} rowKey="id">
144
- <Table.Column
145
- dataIndex="code"
146
- title="Code"
147
- sorter={(a: Resource, b: Resource) => a.code.localeCompare(b.code)}
148
- defaultFilteredValue={getDefaultFilter('code', filters, 'contains')}
149
- filterDropdown={(props) => (
150
- <FilterDropdown
151
- {...props}
152
- clearFilters={() => {
153
- props.setSelectedKeys([]);
154
- props.confirm?.();
155
- }}
156
- >
157
- <Input placeholder="Search Code" />
158
- </FilterDropdown>
159
- )}
160
- filterIcon={<SearchOutlined />}
161
- />
162
- <Table.Column
163
- dataIndex="name"
164
- title="Name"
165
- sorter={(a: Resource, b: Resource) => a.name.localeCompare(b.name)}
166
- defaultFilteredValue={getDefaultFilter('name', filters, 'contains')}
167
- filterDropdown={(props) => (
168
- <FilterDropdown
169
- {...props}
170
- clearFilters={() => {
171
- props.setSelectedKeys([]);
172
- props.confirm?.();
173
- }}
174
- >
175
- <Input placeholder="Search Name" />
176
- </FilterDropdown>
177
- )}
178
- filterIcon={<SearchOutlined />}
179
- />
180
- <Table.Column dataIndex="description" title="Description" />
181
- <Table.Column
182
- dataIndex="creator"
183
- title="Creator"
184
- sorter={(a: Resource, b: Resource) =>
185
- (a.creator || '').localeCompare(b.creator || '')
186
- }
187
- />
188
- <Table.Column
189
- title="Actions"
190
- render={(_, record: Resource) => {
191
- const isProtected = protectedResourcesMap.get(record.id) || false;
192
- return (
193
- <Space>
194
- <Tooltip title="View details">
195
- <Button
196
- type="default"
197
- size="small"
198
- icon={<EyeOutlined />}
199
- onClick={() => setDrawerResource(record)}
200
- />
201
- </Tooltip>
202
- <Tooltip
203
- title={
204
- isProtected
205
- ? 'This is a system resource and cannot be modified'
206
- : 'Edit'
207
- }
208
- >
209
- <Button
210
- type="default"
211
- size="small"
212
- icon={<EditOutlined />}
213
- disabled={isProtected}
214
- onClick={() =>
215
- setShowModal({ type: 'edit', id: record.id })
121
+ <div style={PAGE_WRAPPER_STYLE}>
122
+ <PageHeader
123
+ title="Resource mgnts"
124
+ extra={
125
+ <Button
126
+ type="primary"
127
+ onClick={() =>
128
+ setShowModal({
129
+ type: 'create',
130
+ id: null,
131
+ })
132
+ }
133
+ >
134
+ Create Resource
135
+ </Button>
136
+ }
137
+ />
138
+ <Card className="card-table">
139
+ <Table {...tableProps} rowKey="id">
140
+ <Table.Column
141
+ dataIndex="code"
142
+ title="Code"
143
+ sorter={(a: Resource, b: Resource) => a.code.localeCompare(b.code)}
144
+ defaultFilteredValue={getDefaultFilter('code', filters, 'contains')}
145
+ filterDropdown={(props) => (
146
+ <FilterDropdown
147
+ {...props}
148
+ clearFilters={() => {
149
+ props.setSelectedKeys([]);
150
+ props.confirm?.();
151
+ }}
152
+ >
153
+ <Input placeholder="Search Code" />
154
+ </FilterDropdown>
155
+ )}
156
+ filterIcon={<SearchOutlined />}
157
+ />
158
+ <Table.Column
159
+ dataIndex="name"
160
+ title="Name"
161
+ sorter={(a: Resource, b: Resource) => a.name.localeCompare(b.name)}
162
+ defaultFilteredValue={getDefaultFilter('name', filters, 'contains')}
163
+ filterDropdown={(props) => (
164
+ <FilterDropdown
165
+ {...props}
166
+ clearFilters={() => {
167
+ props.setSelectedKeys([]);
168
+ props.confirm?.();
169
+ }}
170
+ >
171
+ <Input placeholder="Search Name" />
172
+ </FilterDropdown>
173
+ )}
174
+ filterIcon={<SearchOutlined />}
175
+ />
176
+ <Table.Column dataIndex="description" title="Description" />
177
+ <Table.Column
178
+ dataIndex="creator"
179
+ title="Creator"
180
+ sorter={(a: Resource, b: Resource) =>
181
+ (a.creator || '').localeCompare(b.creator || '')
182
+ }
183
+ />
184
+ <Table.Column
185
+ title="Actions"
186
+ render={(_, record: Resource) => {
187
+ const isProtected = protectedResourcesMap.get(record.id) || false;
188
+ return (
189
+ <Space>
190
+ <Tooltip title="View details">
191
+ <Button
192
+ type="default"
193
+ size="small"
194
+ icon={<EyeOutlined />}
195
+ onClick={() => setDrawerResource(record)}
196
+ />
197
+ </Tooltip>
198
+ <Tooltip
199
+ title={
200
+ isProtected
201
+ ? 'This is a system resource and cannot be modified'
202
+ : 'Edit'
216
203
  }
217
- />
218
- </Tooltip>
219
- <Tooltip title="Generate SQL">
220
- <Button
221
- type="default"
222
- size="small"
223
- icon={<CodeOutlined />}
224
- onClick={() => handleShowSql(record)}
225
- />
226
- </Tooltip>
227
- </Space>
228
- );
229
- }}
230
- />
231
- </Table>
204
+ >
205
+ <Button
206
+ type="default"
207
+ size="small"
208
+ icon={<EditOutlined />}
209
+ disabled={isProtected}
210
+ onClick={() =>
211
+ setShowModal({ type: 'edit', id: record.id })
212
+ }
213
+ />
214
+ </Tooltip>
215
+ <Tooltip title="Generate SQL">
216
+ <Button
217
+ type="default"
218
+ size="small"
219
+ icon={<CodeOutlined />}
220
+ onClick={() => handleShowSql(record)}
221
+ />
222
+ </Tooltip>
223
+ </Space>
224
+ );
225
+ }}
226
+ />
227
+ </Table>
228
+ </Card>
232
229
  {showModal.type === 'edit' && showModal.id && (
233
230
  <EditModal
234
231
  id={showModal.id}