|
1 | 1 | import React, { useState } from 'react'
|
2 |
| -import { useLazyGenericGetRequestQuery } from 'src/store/api/app.js' |
3 |
| -import { |
4 |
| - CButton, |
5 |
| - CCard, |
6 |
| - CCardBody, |
7 |
| - CCardHeader, |
8 |
| - CCardTitle, |
9 |
| - CCol, |
10 |
| - CForm, |
11 |
| - CRow, |
12 |
| -} from '@coreui/react' |
13 |
| -import { Form } from 'react-final-form' |
14 |
| -import Skeleton from 'react-loading-skeleton' |
15 |
| -import { RFFCFormSelect } from 'src/components/forms/index.js' |
| 2 | +import { useGenericGetRequestQuery, useLazyGenericGetRequestQuery } from 'src/store/api/app.js' |
| 3 | +import { CButton, CCallout, CCol, CRow, CSpinner } from '@coreui/react' |
| 4 | +import CippChartCard from 'src/components/contentcards/CippChartCard' |
| 5 | +import { CippDatatable, CippTable, cellDateFormatter } from 'src/components/tables' |
| 6 | +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' |
| 7 | +import { CippCallout, CippContentCard } from 'src/components/layout' |
16 | 8 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
17 |
| -import { faLink, faScroll } from '@fortawesome/free-solid-svg-icons' |
18 |
| -import { CippCodeBlock } from 'src/components/utilities/index.js' |
19 |
| -import { Buffer } from 'buffer' |
| 9 | +import CippButtonCard from 'src/components/contentcards/CippButtonCard' |
| 10 | +import { ModalService } from 'src/components/utilities' |
20 | 11 |
|
21 | 12 | /**
|
22 | 13 | * Performs maintenance operations on settings.
|
23 | 14 | *
|
24 | 15 | * @returns {JSX.Element} The JSX element representing the settings maintenance component.
|
25 | 16 | */
|
26 | 17 | export function SettingsMaintenance() {
|
27 |
| - const [selectedScript, setSelectedScript] = useState() |
28 |
| - const [listBackend, listBackendResult] = useLazyGenericGetRequestQuery() |
29 |
| - const [listScript, listScriptResult] = useLazyGenericGetRequestQuery() |
30 |
| - const [listScriptLink, listScriptLinkResult] = useLazyGenericGetRequestQuery() |
| 18 | + const orchestrators = useGenericGetRequestQuery({ |
| 19 | + path: '/api/ExecDurableFunctions', |
| 20 | + params: { Action: 'ListOrchestrators' }, |
| 21 | + }) |
| 22 | + const durableStats = useGenericGetRequestQuery({ |
| 23 | + path: '/api/ExecDurableFunctions', |
| 24 | + params: { Action: 'ListStats' }, |
| 25 | + }) |
31 | 26 |
|
32 |
| - const handleSubmit = async (values) => { |
33 |
| - listScript({ path: 'api/ExecMaintenanceScripts', params: values }) |
34 |
| - setSelectedScript(values.ScriptFile) |
35 |
| - } |
| 27 | + const [resetDurables, resetDurableStatus] = useLazyGenericGetRequestQuery() |
36 | 28 |
|
37 |
| - const handleGetLink = () => { |
38 |
| - listScriptLink({ |
39 |
| - path: 'api/ExecMaintenanceScripts', |
40 |
| - params: { ScriptFile: selectedScript, MakeLink: 'True' }, |
| 29 | + const handleResetDurables = () => { |
| 30 | + ModalService.confirm({ |
| 31 | + title: 'Confirm', |
| 32 | + body: <div>Are you sure you want to reset all Durable Orchestrators?</div>, |
| 33 | + onConfirm: () => |
| 34 | + resetDurables({ |
| 35 | + path: '/api/ExecDurableFunctions', |
| 36 | + params: { Action: 'ResetDurables' }, |
| 37 | + }).then(() => { |
| 38 | + orchestrators.refetch() |
| 39 | + durableStats.refetch() |
| 40 | + }), |
| 41 | + confirmLabel: 'Reset', |
| 42 | + cancelLabel: 'Cancel', |
41 | 43 | })
|
42 | 44 | }
|
| 45 | + |
| 46 | + const ResetButton = ( |
| 47 | + <CButton onClick={handleResetDurables} color="danger"> |
| 48 | + <FontAwesomeIcon icon="trash" /> Reset Durables |
| 49 | + </CButton> |
| 50 | + ) |
| 51 | + |
43 | 52 | return (
|
44 |
| - <> |
45 |
| - {listBackendResult.isUninitialized && listBackend({ path: 'api/ExecMaintenanceScripts' })} |
46 |
| - <CRow> |
47 |
| - <CCol> |
48 |
| - <CCard className="options-card"> |
49 |
| - <CCardHeader> |
50 |
| - <CCardTitle className="d-flex justify-content-between">Maintenance</CCardTitle> |
51 |
| - </CCardHeader> |
52 |
| - <CCardBody> |
53 |
| - <Form |
54 |
| - initialValues={{}} |
55 |
| - onSubmit={handleSubmit} |
56 |
| - render={({ handleSubmit, submitting, values }) => { |
57 |
| - return ( |
58 |
| - <CForm onSubmit={handleSubmit}> |
59 |
| - {listBackendResult.isFetching && ( |
60 |
| - <> |
61 |
| - <CRow> |
62 |
| - <CCol> |
63 |
| - <Skeleton count={5} /> |
64 |
| - </CCol> |
65 |
| - </CRow> |
66 |
| - </> |
67 |
| - )} |
68 |
| - {!listBackendResult.isFetching && listBackendResult.isSuccess && ( |
69 |
| - <> |
70 |
| - <CRow> |
71 |
| - <CCol> |
72 |
| - <RFFCFormSelect |
73 |
| - name="ScriptFile" |
74 |
| - label="Script File" |
75 |
| - placeholder="-- Select a script --" |
76 |
| - values={listBackendResult.data.ScriptFiles} |
77 |
| - /> |
78 |
| - </CCol> |
79 |
| - </CRow> |
80 |
| - <CRow className="mb-3"> |
81 |
| - <CCol> |
82 |
| - <CButton type="submit" disabled={submitting}> |
83 |
| - <FontAwesomeIcon icon={faScroll} className="me-2" /> |
84 |
| - Load Script |
85 |
| - </CButton> |
86 |
| - </CCol> |
87 |
| - </CRow> |
88 |
| - </> |
89 |
| - )} |
90 |
| - </CForm> |
91 |
| - ) |
92 |
| - }} |
93 |
| - /> |
94 |
| - </CCardBody> |
95 |
| - </CCard> |
| 53 | + <div className="mh-100"> |
| 54 | + <CRow className="mb-3"> |
| 55 | + <CCol sm={12} md={5} className="mh-25"> |
| 56 | + <CippChartCard |
| 57 | + title="Durable Functions" |
| 58 | + titleType="big" |
| 59 | + ChartType="bar" |
| 60 | + ChartLabels={durableStats.data?.Queues?.map((queue) => { |
| 61 | + return queue?.Name |
| 62 | + })} |
| 63 | + ChartData={durableStats.data?.Queues?.map((queue) => { |
| 64 | + return queue?.ApproximateMessageCount |
| 65 | + })} |
| 66 | + isFetching={durableStats.isLoading} |
| 67 | + refreshFunction={() => durableStats.refetch()} |
| 68 | + /> |
| 69 | + </CCol> |
| 70 | + <CCol sm={12} md={3} className="mh-25"> |
| 71 | + <CippChartCard |
| 72 | + title="Status" |
| 73 | + titleType="big" |
| 74 | + ChartType="pie" |
| 75 | + ChartLabels={durableStats.data?.Orchestrators?.map((status) => { |
| 76 | + return status.Name |
| 77 | + })} |
| 78 | + ChartData={durableStats?.data?.Orchestrators?.map((status) => { |
| 79 | + return status.Count |
| 80 | + })} |
| 81 | + isFetching={durableStats.isLoading} |
| 82 | + refreshFunction={() => durableStats.refetch()} |
| 83 | + /> |
| 84 | + </CCol> |
| 85 | + <CCol sm={12} md={4}> |
| 86 | + <CippButtonCard title="Troubleshooting" titleType="big" CardButton={ResetButton}> |
| 87 | + <small> |
| 88 | + <p>Use these actions when troubleshooting performance issues with the backend.</p> |
| 89 | + <p> |
| 90 | + <b>NOTE: Resetting durables will terminate any running processes.</b> |
| 91 | + </p> |
| 92 | + </small> |
| 93 | + |
| 94 | + {resetDurableStatus.isFetching && <CSpinner className="ms-2" />} |
| 95 | + {!resetDurableStatus.isFetching && resetDurableStatus.isSuccess && ( |
| 96 | + <CippCallout color="info" dismissible> |
| 97 | + {resetDurableStatus?.data?.Message} |
| 98 | + </CippCallout> |
| 99 | + )} |
| 100 | + </CippButtonCard> |
96 | 101 | </CCol>
|
97 | 102 | </CRow>
|
98 |
| - <CRow> |
99 |
| - <CCol> |
100 |
| - {listScriptResult.isFetching && ( |
101 |
| - <CCard className="h-100"> |
102 |
| - <CCardBody> |
103 |
| - <Skeleton count={10} /> |
104 |
| - </CCardBody> |
105 |
| - </CCard> |
106 |
| - )} |
107 |
| - {!listScriptResult.isFetching && listScriptResult.isSuccess && ( |
108 |
| - <CCard className="h-100"> |
109 |
| - <CCardHeader> |
110 |
| - <CCardTitle>Script Details</CCardTitle> |
111 |
| - </CCardHeader> |
112 |
| - <CCardBody> |
113 |
| - <p> |
114 |
| - <CButton type="submit" onClick={handleGetLink}> |
115 |
| - <FontAwesomeIcon icon={faLink} className="me-2" /> |
116 |
| - Create Link |
117 |
| - </CButton> |
118 |
| - </p> |
119 |
| - {listScriptLinkResult.isSuccess && ( |
120 |
| - <p> |
121 |
| - {listScriptLinkResult.data.Link !== undefined && ( |
122 |
| - <> |
123 |
| - <p> |
124 |
| - Copy this text into a PowerShell terminal, we recommend Azure Cloud Shell. |
125 |
| - Azure modules and the az command line utilties are required for these |
126 |
| - scripts to work. The link is valid for 5 minutes. |
127 |
| - </p> |
128 |
| - <CippCodeBlock |
129 |
| - language="text" |
130 |
| - showLineNumbers={false} |
131 |
| - wrapLongLines={true} |
132 |
| - code={ |
133 |
| - 'irm ' + |
134 |
| - window.location.origin + |
135 |
| - listScriptLinkResult.data.Link + |
136 |
| - ' | iex' |
137 |
| - } |
138 |
| - /> |
139 |
| - </> |
140 |
| - )} |
141 |
| - </p> |
142 |
| - )} |
143 |
| - {listScriptResult.data.ScriptContent !== undefined && ( |
144 |
| - <p> |
145 |
| - <h5>Maintenance Script Contents</h5> |
146 |
| - <CippCodeBlock |
147 |
| - language="powershell" |
148 |
| - showLineNumbers={true} |
149 |
| - wrapLongLines={false} |
150 |
| - code={Buffer.from(listScriptResult.data.ScriptContent, 'base64').toString()} |
151 |
| - /> |
152 |
| - </p> |
153 |
| - )} |
154 |
| - </CCardBody> |
155 |
| - </CCard> |
156 |
| - )} |
| 103 | + <CRow className="mb-3"> |
| 104 | + <CCol className="mh-75"> |
| 105 | + <CippContentCard title="Orchestrators" titleType="big"> |
| 106 | + <CippTable |
| 107 | + data={orchestrators?.data?.Orchestrators} |
| 108 | + columns={[ |
| 109 | + { |
| 110 | + name: 'Created', |
| 111 | + selector: (row) => row['CreatedTime'], |
| 112 | + sortable: true, |
| 113 | + cell: cellDateFormatter({ format: 'short' }), |
| 114 | + }, |
| 115 | + { |
| 116 | + name: 'Completed', |
| 117 | + selector: (row) => row?.CompletedTime, |
| 118 | + sortable: true, |
| 119 | + cell: cellDateFormatter({ format: 'short' }), |
| 120 | + }, |
| 121 | + { |
| 122 | + name: 'Name', |
| 123 | + selector: (row) => row['Name'], |
| 124 | + sortable: true, |
| 125 | + cell: cellGenericFormatter(), |
| 126 | + }, |
| 127 | + { |
| 128 | + name: 'Status', |
| 129 | + selector: (row) => row['RuntimeStatus'], |
| 130 | + sortable: true, |
| 131 | + cell: cellGenericFormatter(), |
| 132 | + }, |
| 133 | + { |
| 134 | + name: 'Input', |
| 135 | + selector: (row) => row['Input'], |
| 136 | + cell: cellGenericFormatter(), |
| 137 | + }, |
| 138 | + ]} |
| 139 | + filterlist={[ |
| 140 | + { filterName: 'Running', filter: 'Complex: RuntimeStatus eq Running' }, |
| 141 | + { filterName: 'Pending', filter: 'Complex: RuntimeStatus eq Pending' }, |
| 142 | + { filterName: 'Completed', filter: 'Complex: RuntimeStatus eq Completed' }, |
| 143 | + { filterName: 'Failed', filter: 'Complex: RuntimeStatus eq Failed' }, |
| 144 | + ]} |
| 145 | + isFetching={orchestrators.isFetching} |
| 146 | + refreshFunction={() => orchestrators.refetch()} |
| 147 | + /> |
| 148 | + </CippContentCard> |
157 | 149 | </CCol>
|
158 | 150 | </CRow>
|
159 |
| - </> |
| 151 | + </div> |
160 | 152 | )
|
161 | 153 | }
|
0 commit comments