@@ -3,10 +3,13 @@ import SettingsIcon from '@mui/icons-material/Settings';
3
3
import CalculateIcon from '@mui/icons-material/Calculate' ;
4
4
import CurrencyExchangeIcon from '@mui/icons-material/CurrencyExchange' ;
5
5
import AddIcon from '@mui/icons-material/Add' ;
6
+ import SaveIcon from '@mui/icons-material/Save' ;
6
7
import Stack from '@mui/material/Stack' ;
7
8
import TextField from '@mui/material/TextField' ;
8
9
import IconButton from '@mui/material/IconButton' ;
9
10
import { enqueueSnackbar } from 'notistack' ;
11
+ import { useFieldArray , useForm } from 'react-hook-form' ;
12
+ import { useCallback , useEffect } from 'react' ;
10
13
import client from '@lib/client' ;
11
14
import AppName from '@lib/appName' ;
12
15
import FullScreenSpinner from '@components/Layout/FullScreenSpinner' ;
@@ -19,6 +22,8 @@ import ExchangeRatesSettingsDialog from '@components/ExchangeRatesSettingsDialog
19
22
import { PolygonApiKeySettingName } from '@lib/getPolygonRates' ;
20
23
import ExchangeRateCalculatorDialog from '@components/ExchangeRateCalculatorDialog' ;
21
24
import RefreshRatesButton from '@components/RefreshRatesButton' ;
25
+ import type { ExchangeRate } from '@server/exchangeRates/types' ;
26
+ import CreateExchangeRateDialog from '@components/CreateExchangeRateDialog' ;
22
27
23
28
export default function ExchangeRatesPage ( ) {
24
29
const { data : polygonApiKey } = client . getValue . useQuery ( {
@@ -51,12 +56,22 @@ export default function ExchangeRatesPage() {
51
56
onOpen : onCalculatorDialogOpen ,
52
57
onClose : onCalculatorDialogClose ,
53
58
} = useDialog ( ) ;
59
+ const {
60
+ open : isUpdateDialogOpen ,
61
+ onOpen : onUpdateDialogOpen ,
62
+ onClose : onUpdateDialogClose ,
63
+ } = useDialog ( ) ;
64
+
65
+ const { handleUpdateRates, isUpdating, formRates, updateRate } =
66
+ useExchangeRatesForm ( rates ) ;
54
67
55
68
let content = null ;
56
69
if ( isLoading ) {
57
70
content = < FullScreenSpinner /> ;
58
71
} else if ( rates && rates . length > 0 ) {
59
- content = < ExchangeRatesTable rates = { rates || [ ] } /> ;
72
+ content = (
73
+ < ExchangeRatesTable rates = { formRates } onUpdateRate = { updateRate } />
74
+ ) ;
60
75
} else {
61
76
content = (
62
77
< EmptyState Icon = { CurrencyExchangeIcon } >
@@ -88,6 +103,9 @@ export default function ExchangeRatesPage() {
88
103
< IconButton color = "primary" disabled = { ! rates } >
89
104
< CalculateIcon onClick = { onCalculatorDialogOpen } />
90
105
</ IconButton >
106
+ < IconButton color = "primary" disabled = { isUpdating } >
107
+ < SaveIcon onClick = { handleUpdateRates } />
108
+ </ IconButton >
91
109
< RefreshRatesButton />
92
110
< IconButton color = "primary" >
93
111
< SettingsIcon onClick = { onSettingsDialogOpen } />
@@ -104,17 +122,73 @@ export default function ExchangeRatesPage() {
104
122
onUpdate = { updateValue }
105
123
/>
106
124
) }
107
- { isCalculatorDialogOpen && rates && (
125
+ { isCalculatorDialogOpen && formRates && (
108
126
< ExchangeRateCalculatorDialog
109
127
open = { isCalculatorDialogOpen }
110
128
onClose = { onCalculatorDialogClose }
111
- rates = { rates }
129
+ rates = { formRates }
130
+ />
131
+ ) }
132
+ { isUpdateDialogOpen && formRates && (
133
+ < CreateExchangeRateDialog
134
+ open = { isUpdateDialogOpen }
135
+ onClose = { onUpdateDialogClose }
112
136
/>
113
137
) }
114
138
</ Stack >
115
- < Fab aria-label = "New rate" >
139
+ < Fab aria-label = "New rate" onClick = { onUpdateDialogOpen } >
116
140
< AddIcon />
117
141
</ Fab >
118
142
</ >
119
143
) ;
120
144
}
145
+
146
+ type ExchangeRatesFormValues = {
147
+ rates : ExchangeRate [ ] ;
148
+ } ;
149
+
150
+ const useExchangeRatesForm = ( rates : ExchangeRate [ ] | undefined ) => {
151
+ const { mutate : updateExchangeRates , isPending : isUpdating } =
152
+ client . updateExchangeRates . useMutation ( {
153
+ onSuccess : ( ) => {
154
+ enqueueSnackbar ( {
155
+ message : 'Exchange rates updated.' ,
156
+ variant : 'success' ,
157
+ } ) ;
158
+ } ,
159
+ onError : ( e ) => {
160
+ enqueueSnackbar ( {
161
+ message : `Failed to update exchange rates. ${ e . message } ` ,
162
+ variant : 'error' ,
163
+ } ) ;
164
+ } ,
165
+ } ) ;
166
+
167
+ const { control } = useForm < ExchangeRatesFormValues > ( {
168
+ mode : 'onBlur' ,
169
+ defaultValues : {
170
+ rates : rates || [ ] ,
171
+ } ,
172
+ } ) ;
173
+ const {
174
+ fields : formRates ,
175
+ update : updateRate ,
176
+ replace : replaceRates ,
177
+ } = useFieldArray ( {
178
+ control,
179
+ name : 'rates' ,
180
+ } ) ;
181
+
182
+ useEffect ( ( ) => replaceRates ( rates || [ ] ) , [ rates , replaceRates ] ) ;
183
+
184
+ const handleUpdateRates = useCallback ( ( ) => {
185
+ updateExchangeRates ( formRates ) ;
186
+ } , [ formRates , updateExchangeRates ] ) ;
187
+
188
+ return {
189
+ handleUpdateRates,
190
+ isUpdating,
191
+ formRates,
192
+ updateRate,
193
+ } ;
194
+ } ;
0 commit comments