Skip to content

Commit d7e2926

Browse files
authored
feat(cluster): add parameter groups management and parameters management (#57)
1 parent b22613d commit d7e2926

63 files changed

Lines changed: 3656 additions & 12 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { renderRange } from '@apps/main/[3]cluster/[3]param-group/components/EditableParamCard/helper'
Lines changed: 368 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,377 @@
1-
import { ClusterInfo } from '@/api/model'
2-
import { loadI18n } from '@i18n-macro'
1+
import { useEffect, useMemo, useState } from 'react'
2+
import { ProColumns } from '@ant-design/pro-table'
3+
import {
4+
Button,
5+
Form,
6+
FormInstance,
7+
message,
8+
Modal,
9+
Space,
10+
Table,
11+
TableColumnsType,
12+
} from 'antd'
13+
import {
14+
// EditOutlined,
15+
ExclamationCircleOutlined,
16+
QuestionCircleOutlined,
17+
RollbackOutlined,
18+
SaveOutlined,
19+
} from '@ant-design/icons'
20+
import HeavyTable from '@/components/HeavyTable'
21+
import { ClusterInfo, ClusterParamItem } from '@/api/model'
22+
import {
23+
invalidateClusterParams,
24+
useQueryClusterParams,
25+
useUpdateClusterParams,
26+
} from '@/api/hooks/cluster'
27+
import IntlPopConfirm from '@/components/IntlPopConfirm'
28+
import { useQueryClient } from 'react-query'
29+
import { TFunction } from 'react-i18next'
30+
import { loadI18n, useI18n } from '@i18n-macro'
31+
import { Link } from 'react-router-dom'
32+
import { resolveRoute } from '@pages-macro'
33+
import { isArray } from '@/utils/types'
34+
import { renderRange } from './helper'
35+
// import { usePagination } from '@hooks/usePagination'
336

437
loadI18n()
538

639
export interface ParamsTableProps {
740
cluster: ClusterInfo
841
}
942

43+
const getRowKey = (record: ClusterParamItem) => record.paramId!
44+
1045
export function ParamsTable({ cluster }: ParamsTableProps) {
11-
return <>{cluster.clusterId}</>
46+
const { t, i18n } = useI18n()
47+
const {
48+
paramGroupId,
49+
paramList: originalData,
50+
refetch,
51+
isLoading,
52+
} = useFetchParamsData(cluster.clusterId!)
53+
54+
const [tableData, setTableData] = useState<ClusterParamItem[]>([])
55+
56+
useEffect(() => {
57+
!isLoading && setTableData(originalData!)
58+
}, [isLoading, originalData])
59+
60+
const [form] = Form.useForm()
61+
62+
const queryClient = useQueryClient()
63+
const updateParams = useUpdateClusterParams()
64+
65+
const handleSave = () => {
66+
const changes = findParamsChanges(originalData!, tableData)
67+
if (changes.length === 0) {
68+
message.info(t('save.nochanges'))
69+
} else {
70+
const rebootNeeded = changes.some((change) => change.reboot)
71+
72+
Modal.confirm({
73+
title: t('save.confirm'),
74+
icon: <ExclamationCircleOutlined />,
75+
width: 600,
76+
content: <ConfirmTable changes={changes} />,
77+
okText: rebootNeeded
78+
? t('save.okText.reboot')
79+
: t('save.okText.normal'),
80+
async onOk() {
81+
updateParams.mutateAsync(
82+
{
83+
payload: {
84+
clusterId: cluster.clusterId!,
85+
params: changes.map((change) => ({
86+
paramId: change.paramId,
87+
realValue: {
88+
clusterValue: change.change[1],
89+
},
90+
})),
91+
// TODO
92+
reboot: rebootNeeded,
93+
},
94+
options: {
95+
actionName: t('save.name'),
96+
},
97+
},
98+
{
99+
onSettled() {
100+
invalidateClusterParams(queryClient, cluster.clusterId!)
101+
},
102+
}
103+
)
104+
},
105+
})
106+
}
107+
}
108+
109+
const toolbar = () => [
110+
<IntlPopConfirm
111+
key="reset"
112+
title={t('reset.confirm')}
113+
icon={<QuestionCircleOutlined style={{ color: 'red' }} />}
114+
onConfirm={() => {
115+
setTableData(originalData!)
116+
}}
117+
>
118+
<Button>
119+
<RollbackOutlined /> {t('actions.reset')}
120+
</Button>
121+
</IntlPopConfirm>,
122+
<Button type="primary" key="submit" onClick={handleSave}>
123+
<SaveOutlined /> {t('actions.save')}
124+
</Button>,
125+
]
126+
127+
const columns = useMemo(() => getColumns(t, form), [form, i18n.language])
128+
129+
return (
130+
<HeavyTable
131+
loading={isLoading}
132+
dataSource={tableData}
133+
headerTitle={
134+
paramGroupId ? (
135+
<Space size="middle">
136+
<span>{t('header.title')}</span>
137+
<Link
138+
to={`${resolveRoute('../../../')}/param-group/${paramGroupId}`}
139+
>
140+
{paramGroupId}
141+
</Link>
142+
</Space>
143+
) : (
144+
''
145+
)
146+
}
147+
tooltip={false}
148+
columns={columns}
149+
pagination={false}
150+
scroll={{ x: 1600, y: 700 }}
151+
options={{
152+
density: false,
153+
fullScreen: false,
154+
setting: false,
155+
reload: () => refetch(),
156+
}}
157+
rowKey={getRowKey}
158+
toolBarRender={toolbar}
159+
editable={{
160+
type: 'single',
161+
form,
162+
saveText: t('actions.confirm'),
163+
actionRender: (_, __, dom) => [dom.save, dom.cancel],
164+
onSave: async (paramId, editedRow) => {
165+
setTableData((prev) =>
166+
prev.map((row) => {
167+
if (row.paramId !== paramId) return row
168+
169+
return {
170+
...row,
171+
realValue: {
172+
...row.realValue,
173+
clusterValue:
174+
editedRow.realValue?.clusterValue || row.defaultValue,
175+
},
176+
}
177+
})
178+
)
179+
},
180+
}}
181+
/>
182+
)
183+
}
184+
185+
function getColumns(t: TFunction<''>, form: FormInstance) {
186+
const columns: ProColumns<ClusterParamItem>[] = [
187+
{
188+
title: t('model:clusterParam.property.component'),
189+
width: 100,
190+
fixed: 'left',
191+
dataIndex: 'instanceType',
192+
editable: false,
193+
},
194+
{
195+
title: t('model:clusterParam.property.category'),
196+
width: 100,
197+
fixed: 'left',
198+
dataIndex: 'category',
199+
editable: false,
200+
},
201+
{
202+
title: t('model:clusterParam.property.name'),
203+
width: 160,
204+
fixed: 'left',
205+
dataIndex: 'name',
206+
editable: false,
207+
},
208+
{
209+
title: t('model:clusterParam.property.reboot'),
210+
width: 80,
211+
key: 'hasReboot',
212+
render(_, record) {
213+
return record.hasReboot!
214+
? t('model:clusterParam.reboot.true')
215+
: t('model:clusterParam.reboot.false')
216+
},
217+
editable: false,
218+
},
219+
{
220+
title: t('model:clusterParam.property.range'),
221+
width: 180,
222+
key: 'range',
223+
renderText(_, record) {
224+
return isArray(record.range) && record.range.length > 0
225+
? renderRange(record.type!, record.range)
226+
: null
227+
},
228+
editable: false,
229+
},
230+
{
231+
title: t('model:clusterParam.property.default'),
232+
width: 120,
233+
dataIndex: 'defaultValue',
234+
editable: false,
235+
},
236+
{
237+
title: t('model:clusterParam.property.current'),
238+
width: 120,
239+
dataIndex: ['realValue', 'clusterValue'],
240+
// render(_, record, __, action) {
241+
// return (
242+
// <span
243+
// onClick={() => {
244+
// form.resetFields()
245+
// action?.startEditable(record.paramId!)
246+
// }}
247+
// style={{ cursor: 'pointer' }}
248+
// >
249+
// <EditOutlined /> {record.realValue?.clusterValue}
250+
// </span>
251+
// )
252+
// },
253+
},
254+
{
255+
title: t('columns.actions'),
256+
valueType: 'option',
257+
width: 100,
258+
render: (_, record, __, action) => {
259+
return (
260+
<a
261+
key="editable"
262+
onClick={() => {
263+
form.resetFields()
264+
action?.startEditable?.(record.paramId!)
265+
}}
266+
>
267+
{t('actions.edit')}
268+
</a>
269+
)
270+
},
271+
},
272+
{
273+
title: t('model:clusterParam.property.desc'),
274+
dataIndex: 'description',
275+
editable: false,
276+
},
277+
]
278+
return columns
279+
}
280+
281+
function useFetchParamsData(id: string) {
282+
// const [pagination, setPagination] = usePagination(40)
283+
const { data, isLoading, refetch } = useQueryClusterParams(
284+
{
285+
id,
286+
},
287+
{ refetchOnWindowFocus: false }
288+
)
289+
const result = data?.data.data
290+
291+
return {
292+
// pagination,
293+
// setPagination,
294+
paramGroupId: result?.paramGroupId,
295+
paramList: result?.params,
296+
isLoading,
297+
refetch,
298+
}
299+
}
300+
301+
type Change = {
302+
paramId: string
303+
component: string
304+
name: string
305+
change: [string, string]
306+
reboot: number
307+
}
308+
309+
function findParamsChanges(
310+
origin: ClusterParamItem[],
311+
table: ClusterParamItem[]
312+
) {
313+
const changes: Change[] = []
314+
origin.forEach((raw, i) => {
315+
if (raw.realValue?.clusterValue !== table[i].realValue?.clusterValue) {
316+
changes.push({
317+
paramId: raw.paramId!,
318+
component: raw.instanceType!,
319+
name: raw.name!,
320+
change: [
321+
raw.realValue!.clusterValue!,
322+
table[i].realValue!.clusterValue!,
323+
],
324+
reboot: raw.hasReboot!,
325+
})
326+
}
327+
})
328+
return changes
329+
}
330+
331+
function ConfirmTable({ changes }: { changes: Change[] }) {
332+
const { t, i18n } = useI18n()
333+
const columns: TableColumnsType<Change> = useMemo(
334+
() => [
335+
{
336+
title: t('save.fields.component'),
337+
dataIndex: 'component',
338+
key: 'component',
339+
},
340+
{
341+
title: t('save.fields.name'),
342+
dataIndex: 'name',
343+
key: 'name',
344+
},
345+
{
346+
title: t('save.fields.before'),
347+
key: 'before',
348+
render: (_, record) => record.change[0],
349+
},
350+
{
351+
title: t('save.fields.after'),
352+
key: 'after',
353+
render: (_, record) => record.change[1],
354+
},
355+
{
356+
title: t('save.fields.reboot'),
357+
key: 'reboot',
358+
render(_, record) {
359+
return record.reboot!
360+
? t('model:clusterParam.reboot.true')
361+
: t('model:clusterParam.reboot.false')
362+
},
363+
},
364+
],
365+
[i18n.language]
366+
)
367+
return (
368+
<Table
369+
dataSource={changes}
370+
pagination={false}
371+
rowKey="name"
372+
size="small"
373+
columns={columns}
374+
scroll={{ y: 400 }}
375+
/>
376+
)
12377
}

0 commit comments

Comments
 (0)