back-testing-react 1.0.4 → 1.0.6
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.
- package/dist/cjs/index.js +17 -17
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +17 -17
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +35 -10
- package/package.json +5 -1
- package/src/components/back-testing-cat-legend/back-testing-cat-legend.css +1 -1
- package/src/components/back-testing-cat-legend/back-testing-cat-legend.tsx +7 -7
- package/src/components/back-testing-map/back-testing-map.css +1 -1
- package/src/components/back-testing-map/back-testing-map.service.ts +21 -5
- package/src/components/back-testing-map/back-testing-map.tsx +51 -18
- package/src/components/back-testing-map/back-testing-map.types.ts +74 -11
- package/src/components/back-testing-payout-info/back-testing-payout-info.css +112 -0
- package/src/components/back-testing-payout-info/back-testing-payout-info.tsx +371 -0
- package/src/components/back-testing-payout-info/back-testing-payout-info.types.ts +9 -0
- package/src/components/back-testing-stepper/back-testing-stepper.css +30 -0
- package/src/components/back-testing-stepper/back-testing-stepper.tsx +322 -0
- package/src/components/back-testing-stepper/back-testing-stepper.types.ts +41 -0
- package/src/components/back-testing-stepper/inputs/distance-input/distance-input.css +57 -0
- package/src/components/back-testing-stepper/inputs/distance-input/distance-input.stories.tsx +20 -0
- package/src/components/back-testing-stepper/inputs/distance-input/distance-input.tsx +93 -0
- package/src/components/back-testing-stepper/inputs/distance-input/distance-input.types.ts +41 -0
- package/src/components/back-testing-stepper/inputs/file-input/file-input.css +26 -0
- package/src/components/back-testing-stepper/inputs/file-input/file-input.stories.tsx +20 -0
- package/src/components/back-testing-stepper/inputs/file-input/file-input.tsx +43 -0
- package/src/components/back-testing-stepper/inputs/file-input/file-input.types.ts +10 -0
- package/src/components/back-testing-stepper/inputs/number-input/number-input.css +48 -0
- package/src/components/back-testing-stepper/inputs/number-input/number-input.stories.tsx +20 -0
- package/src/components/back-testing-stepper/inputs/number-input/number-input.tsx +55 -0
- package/src/components/back-testing-stepper/inputs/number-input/number-input.types.ts +12 -0
- package/src/components/back-testing-stepper/inputs/select-input/select-input.css +13 -0
- package/src/components/back-testing-stepper/inputs/select-input/select-input.stories.tsx +27 -0
- package/src/components/back-testing-stepper/inputs/select-input/select-input.tsx +63 -0
- package/src/components/back-testing-stepper/inputs/select-input/select-input.types.ts +8 -0
- package/src/components/back-testing-stepper/inputs/text-input/text-input.css +35 -0
- package/src/components/back-testing-stepper/inputs/text-input/text-input.stories.tsx +18 -0
- package/src/components/back-testing-stepper/inputs/text-input/text-input.tsx +32 -0
- package/src/components/back-testing-stepper/inputs/text-input/text-input.types.ts +6 -0
- package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.css +16 -0
- package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.stories.tsx +20 -0
- package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.tsx +151 -0
- package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.types.ts +11 -0
- package/src/components/back-testing-stepper/steps/input-anemometer/input-anemometer.css +0 -0
- package/src/components/back-testing-stepper/steps/input-anemometer/input-anemometer.tsx +188 -0
- package/src/components/back-testing-stepper/steps/input-anemometer/input-anemometer.types.ts +16 -0
- package/src/components/back-testing-stepper/steps/input-ciac/input-ciac.scss +0 -0
- package/src/components/back-testing-stepper/steps/input-ciac/input-ciac.tsx +225 -0
- package/src/components/back-testing-stepper/steps/input-ciac/input-ciac.types.ts +17 -0
- package/src/components/back-testing-stepper/steps/input-location/input-location.css +0 -0
- package/src/components/back-testing-stepper/steps/input-location/input-location.tsx +135 -0
- package/src/components/back-testing-stepper/steps/input-location/input-location.types.ts +16 -0
- package/src/components/back-testing-stepper/steps/input-proxy/input-proxy.css +0 -0
- package/src/components/back-testing-stepper/steps/input-proxy/input-proxy.tsx +173 -0
- package/src/components/back-testing-stepper/steps/input-proxy/input-proxy.types.ts +16 -0
- package/src/components/back-testing-stepper/steps/step.types.ts +58 -0
- package/src/components/back-testing-storm-legend/back-testing-storm-legend.stories.tsx +56 -33
- package/src/components/back-testing-wizard/back-testing-wizard.css +36 -0
- package/src/components/back-testing-wizard/back-testing-wizard.stories.tsx +734 -0
- package/src/components/back-testing-wizard/back-testing-wizard.tsx +102 -0
- package/src/components/back-testing-wizard/back-testing-wizard.types.ts +12 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { BackTestingStepperProps } from "./back-testing-stepper.types"
|
|
2
|
+
import "./back-testing-stepper.css"
|
|
3
|
+
import { PayoutWizardStep, PayoutWizardStepType, RadioChoice, WizardStep } from "./steps/step.types";
|
|
4
|
+
import mapboxgl, { GeoJSONSource } from 'mapbox-gl';
|
|
5
|
+
import React, { useEffect, useRef } from "react";
|
|
6
|
+
import { AnemometerRequest, CIAC_LAYER, CIACRequest, DEFAULT_MAP_COORDINATE, DEFAULT_MAP_ZOOM, generateAnemometerRequest, generateCIACRequest, generateInitialPayoutRequest, generateProxyRequest, PayoutRequest, ProxyRequest, TROPICAL_STORM_LAYER, TROPICAL_STORM_TRACK_LAYER } from "../back-testing-map/back-testing-map.types";
|
|
7
|
+
import InputLocationStep from "./steps/input-location/input-location";
|
|
8
|
+
import InputProxyStep from "./steps/input-proxy/input-proxy";
|
|
9
|
+
import InputAnemometerStep from "./steps/input-anemometer/input-anemometer";
|
|
10
|
+
import InputCIACStep from "./steps/input-ciac/input-ciac";
|
|
11
|
+
import Stepper from '@mui/material/Stepper';
|
|
12
|
+
import Step from '@mui/material/Step';
|
|
13
|
+
import StepLabel from '@mui/material/StepLabel';
|
|
14
|
+
import StepContent from '@mui/material/StepContent';
|
|
15
|
+
import { Box, Button, Switch, Typography } from "@mui/material";
|
|
16
|
+
import BackTestingPayoutInfo from "../back-testing-payout-info/back-testing-payout-info";
|
|
17
|
+
|
|
18
|
+
const BackTestingStepper = (props: BackTestingStepperProps) => {
|
|
19
|
+
|
|
20
|
+
const label = { inputProps: { 'aria-label': 'Step Switch' } };
|
|
21
|
+
const [payoutRequest, setPayoutRequest] = React.useState<PayoutRequest>(props.payoutRequest);
|
|
22
|
+
const [proxy, setProxy] = React.useState<ProxyRequest>(payoutRequest.proxy);
|
|
23
|
+
const [anemometer, setAnemometer] = React.useState<AnemometerRequest>(payoutRequest.anemometer);
|
|
24
|
+
const [ciac, setCIAC] = React.useState<CIACRequest>(payoutRequest.ciac);
|
|
25
|
+
|
|
26
|
+
const handleStepToggle = (step:PayoutWizardStep, val:boolean) => {
|
|
27
|
+
if(step.step == PayoutWizardStepType.INPUT_PROXY_DETAILS){
|
|
28
|
+
step.enabled[1](val);
|
|
29
|
+
let proxy : ProxyRequest = generateProxyRequest(
|
|
30
|
+
[payoutRequest.proxy.longitude,payoutRequest.proxy.latitude],
|
|
31
|
+
payoutRequest.proxy.radius,
|
|
32
|
+
payoutRequest.proxy.payouts,
|
|
33
|
+
val
|
|
34
|
+
);
|
|
35
|
+
setProxy(proxy);
|
|
36
|
+
} else if(step.step == PayoutWizardStepType.INPUT_ANEMOMETER_DETAILS){
|
|
37
|
+
step.enabled[1](val);
|
|
38
|
+
let anemometer : AnemometerRequest = generateAnemometerRequest(
|
|
39
|
+
[payoutRequest.anemometer.longitude,payoutRequest.anemometer.latitude],
|
|
40
|
+
payoutRequest.anemometer.code,
|
|
41
|
+
payoutRequest.anemometer.payouts,
|
|
42
|
+
val
|
|
43
|
+
);
|
|
44
|
+
setAnemometer(anemometer);
|
|
45
|
+
} else if(step.step == PayoutWizardStepType.INPUT_CIAC_DETAILS){
|
|
46
|
+
step.enabled[1](val);
|
|
47
|
+
let ciac : CIACRequest = generateCIACRequest(
|
|
48
|
+
[payoutRequest.longitude,payoutRequest.latitude],
|
|
49
|
+
payoutRequest.ciac.shapes[0].radius,
|
|
50
|
+
payoutRequest.ciac.includeNotNamedStorms,
|
|
51
|
+
payoutRequest.ciac.payouts,
|
|
52
|
+
val
|
|
53
|
+
);
|
|
54
|
+
setCIAC(ciac);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// simplified step wizard
|
|
59
|
+
const simplifiedSteps : PayoutWizardStep[] = [
|
|
60
|
+
{
|
|
61
|
+
step: PayoutWizardStepType.SIMPLIFIED_INPUT_LOCATION,
|
|
62
|
+
label: 'Input Risk Details',
|
|
63
|
+
description: `Input your risk location address, the corresponding limit of insurance, and the desired CIAC radius.`,
|
|
64
|
+
componentRef: useRef<WizardStep>(null),
|
|
65
|
+
istoggable:false,
|
|
66
|
+
enabled:React.useState(true)
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
// extended step wizard
|
|
71
|
+
const extendedSteps : PayoutWizardStep[] = [
|
|
72
|
+
{
|
|
73
|
+
step: PayoutWizardStepType.INPUT_LOCATION,
|
|
74
|
+
label: 'Risk Location',
|
|
75
|
+
description: `Input your risk location address and corresponding limit of insurance.`,
|
|
76
|
+
componentRef: useRef<WizardStep>(null),
|
|
77
|
+
istoggable:false,
|
|
78
|
+
enabled:React.useState(true)
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
step: PayoutWizardStepType.INPUT_PROXY_DETAILS,
|
|
82
|
+
label: 'Proxy Details',
|
|
83
|
+
description: `Choose between a preconfigured payout table or upload a custom table.`,
|
|
84
|
+
componentRef: useRef<WizardStep>(null),
|
|
85
|
+
istoggable:true,
|
|
86
|
+
enabled:React.useState(payoutRequest.proxy.enabled)
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
step: PayoutWizardStepType.INPUT_ANEMOMETER_DETAILS,
|
|
90
|
+
label: 'Anemometer Details',
|
|
91
|
+
description: `Input the anemometer station ID and choose between a preconfigured payout table or upload a custom table.`,
|
|
92
|
+
componentRef: useRef<WizardStep>(null),
|
|
93
|
+
istoggable:true,
|
|
94
|
+
enabled:React.useState(payoutRequest.anemometer.enabled)
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
step: PayoutWizardStepType.INPUT_CIAC_DETAILS,
|
|
98
|
+
label: 'CIAC Details',
|
|
99
|
+
description: `Choose between a preconfigured payout table or upload a custom table.`,
|
|
100
|
+
componentRef: useRef<WizardStep>(null),
|
|
101
|
+
istoggable:true,
|
|
102
|
+
enabled:React.useState(payoutRequest.ciac.enabled)
|
|
103
|
+
}
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const steps = extendedSteps
|
|
107
|
+
const [activeStep, setActiveStep] = React.useState(0);
|
|
108
|
+
const [locationMarker, setLocationMarker] = React.useState<mapboxgl.Marker>(new mapboxgl.Marker());
|
|
109
|
+
const [wizardStep, setWizardStep] = React.useState<PayoutWizardStepType>(props.currentStep);
|
|
110
|
+
const [locationAddress, setLocationAddress] = React.useState<string>("");
|
|
111
|
+
const [proxyChoice, setProxyChoice] = React.useState<RadioChoice>(props.proxyChoice);
|
|
112
|
+
const [anemometerChoice, setAnemometerChoice] = React.useState<RadioChoice>(props.proxyChoice);
|
|
113
|
+
const [ciacChoice, setCIACChoice] = React.useState<RadioChoice>(props.proxyChoice);
|
|
114
|
+
const [selectedProxyPayoutOption, setProxyPayoutOption] = React.useState<number>(props.selectedProxyPayoutOptionIndex);
|
|
115
|
+
const [selectedAnemometerPayoutOption, setAnemometerPayoutOption] = React.useState<number>(props.selectedAnemometerPayoutOptionIndex);
|
|
116
|
+
const [selectedCIACPayoutOption, setCIACPayoutOption] = React.useState<number>(props.selectedCIACPayoutOptionIndex);
|
|
117
|
+
|
|
118
|
+
const handleProxyPayoutOptionChange = (e:number) => {
|
|
119
|
+
setProxyPayoutOption(e)
|
|
120
|
+
props.onProxyPayoutOptionChange(e);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const handleAnemometerPayoutOptionChange = (e:number) => {
|
|
124
|
+
setAnemometerPayoutOption(e)
|
|
125
|
+
props.onAnemometerPayoutOptionChange(e);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const handleCIACPayoutOptionChange = (e:number) => {
|
|
129
|
+
setCIACPayoutOption(e)
|
|
130
|
+
props.onCIACPayoutOptionChange(e);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
let request : PayoutRequest = {
|
|
135
|
+
includeZeroPayouts: payoutRequest.includeZeroPayouts,
|
|
136
|
+
limit: payoutRequest.limit,
|
|
137
|
+
latitude: payoutRequest.latitude,
|
|
138
|
+
longitude: payoutRequest.longitude,
|
|
139
|
+
proxy: proxy,
|
|
140
|
+
anemometer: anemometer,
|
|
141
|
+
ciac: ciac
|
|
142
|
+
}
|
|
143
|
+
setPayoutRequest(request);
|
|
144
|
+
props.onPayoutRequestChange(request);
|
|
145
|
+
}, [ciac,proxy,anemometer])
|
|
146
|
+
|
|
147
|
+
const handleNext = (step:{ step: PayoutWizardStepType, label: string, description: string, componentRef: React.RefObject<WizardStep>}) => {
|
|
148
|
+
step.componentRef.current?.handleNext();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const handleBack = (step:{ step: PayoutWizardStepType, label: string, description: string, componentRef: React.RefObject<WizardStep>}) => {
|
|
152
|
+
step.componentRef.current?.handleBack();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const handleReset = (step: PayoutWizardStepType) => {
|
|
156
|
+
setLocationAddress("");
|
|
157
|
+
setWizardStep(step);
|
|
158
|
+
if(props.onStormPayoutsChanged != undefined){
|
|
159
|
+
props.onStormPayoutsChanged([])
|
|
160
|
+
}
|
|
161
|
+
props.onCurrentStepChange(step);
|
|
162
|
+
props.onProxyChoiceChange(RadioChoice.DEFAULT);
|
|
163
|
+
props.onAnemometerChoiceChange(RadioChoice.DEFAULT);
|
|
164
|
+
props.onCIACChoiceChange(RadioChoice.DEFAULT);
|
|
165
|
+
props.onProxyPayoutOptionChange(0);
|
|
166
|
+
props.onAnemometerPayoutOptionChange(0);
|
|
167
|
+
props.onCIACPayoutOptionChange(0);
|
|
168
|
+
locationMarker.remove()
|
|
169
|
+
setActiveStep(0);
|
|
170
|
+
let resetPayoutRequest = generateInitialPayoutRequest();
|
|
171
|
+
setPayoutRequest(resetPayoutRequest);
|
|
172
|
+
props.onPayoutRequestChange(resetPayoutRequest);
|
|
173
|
+
const catSource = (props.mapInstanceRef.current as mapboxgl.Map).getSource(CIAC_LAYER)
|
|
174
|
+
if (catSource){
|
|
175
|
+
(catSource as GeoJSONSource).setData({type: 'FeatureCollection', features: []})
|
|
176
|
+
}
|
|
177
|
+
const circleSource = (props.mapInstanceRef.current as mapboxgl.Map).getSource(TROPICAL_STORM_LAYER)
|
|
178
|
+
if (circleSource){
|
|
179
|
+
(circleSource as GeoJSONSource).setData({type: 'FeatureCollection', features: []})
|
|
180
|
+
}
|
|
181
|
+
const lineSource = (props.mapInstanceRef.current as mapboxgl.Map).getSource(TROPICAL_STORM_TRACK_LAYER)
|
|
182
|
+
if (lineSource){
|
|
183
|
+
(lineSource as GeoJSONSource).setData({type: 'FeatureCollection', features: []})
|
|
184
|
+
}
|
|
185
|
+
(props.mapInstanceRef.current as mapboxgl.Map).flyTo({
|
|
186
|
+
center: DEFAULT_MAP_COORDINATE,
|
|
187
|
+
essential: true,
|
|
188
|
+
zoom: DEFAULT_MAP_ZOOM})
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
function renderStepInputs(step:{ step: PayoutWizardStepType, label: string, description: string, componentRef: React.MutableRefObject<any>}) {
|
|
192
|
+
switch(step.step) {
|
|
193
|
+
case PayoutWizardStepType.INPUT_LOCATION:
|
|
194
|
+
return <>
|
|
195
|
+
<InputLocationStep
|
|
196
|
+
ref={step.componentRef}
|
|
197
|
+
accessToken={props.mapAccessToken}
|
|
198
|
+
mapInstanceRef={props.mapInstanceRef}
|
|
199
|
+
marker={locationMarker}
|
|
200
|
+
activeStep={activeStep}
|
|
201
|
+
payoutRequest={payoutRequest}
|
|
202
|
+
address={locationAddress}
|
|
203
|
+
onActiveStepChange={setActiveStep}
|
|
204
|
+
onStepChange={setWizardStep}
|
|
205
|
+
onLocationAddressChange={setLocationAddress}
|
|
206
|
+
onPayoutRequestChange={setPayoutRequest}/>
|
|
207
|
+
</>;
|
|
208
|
+
case PayoutWizardStepType.INPUT_PROXY_DETAILS:
|
|
209
|
+
return <>
|
|
210
|
+
<InputProxyStep
|
|
211
|
+
ref={step.componentRef}
|
|
212
|
+
mapInstanceRef={props.mapInstanceRef}
|
|
213
|
+
activeStep={activeStep}
|
|
214
|
+
proxyChoice={proxyChoice}
|
|
215
|
+
request={payoutRequest}
|
|
216
|
+
onActiveStepChange={setActiveStep}
|
|
217
|
+
onStepChange={setWizardStep}
|
|
218
|
+
payoutOptions={props.proxyPayoutOptions}
|
|
219
|
+
selectedPayoutOptionIndex={selectedProxyPayoutOption}
|
|
220
|
+
onPayoutOptionChange={handleProxyPayoutOptionChange}
|
|
221
|
+
onProxyChoiceChange={setProxyChoice}
|
|
222
|
+
onProxyRequestChange={setProxy}
|
|
223
|
+
/>
|
|
224
|
+
</>;
|
|
225
|
+
case PayoutWizardStepType.INPUT_ANEMOMETER_DETAILS:
|
|
226
|
+
return <>
|
|
227
|
+
<InputAnemometerStep
|
|
228
|
+
ref={step.componentRef}
|
|
229
|
+
mapInstanceRef={props.mapInstanceRef}
|
|
230
|
+
activeStep={activeStep}
|
|
231
|
+
request={payoutRequest}
|
|
232
|
+
anemometerChoice={anemometerChoice}
|
|
233
|
+
selectedPayoutOptionIndex={selectedAnemometerPayoutOption}
|
|
234
|
+
payoutOptions={props.anemometerPayoutOptions}
|
|
235
|
+
onActiveStepChange={setActiveStep}
|
|
236
|
+
onStepChange={setWizardStep}
|
|
237
|
+
onAnemometerChoiceChange={setAnemometerChoice}
|
|
238
|
+
onPayoutOptionChange={handleAnemometerPayoutOptionChange}
|
|
239
|
+
onAnemometerRequestChange={setAnemometer}/>
|
|
240
|
+
</>;
|
|
241
|
+
case PayoutWizardStepType.INPUT_CIAC_DETAILS:
|
|
242
|
+
return <>
|
|
243
|
+
<InputCIACStep
|
|
244
|
+
ref={step.componentRef}
|
|
245
|
+
mapInstanceRef={props.mapInstanceRef}
|
|
246
|
+
activeStep={activeStep}
|
|
247
|
+
request={payoutRequest}
|
|
248
|
+
ciacChoice={ciacChoice}
|
|
249
|
+
selectedPayoutOptionIndex={selectedCIACPayoutOption}
|
|
250
|
+
payoutOptions={props.ciacPayoutOptions}
|
|
251
|
+
onActiveStepChange={setActiveStep}
|
|
252
|
+
onStepChange={setWizardStep}
|
|
253
|
+
onCIACChoiceChange={setCIACChoice}
|
|
254
|
+
onPayoutOptionChange={handleCIACPayoutOptionChange}
|
|
255
|
+
onCalculatePayoutEnabledChanged={props.onCalculatePayoutEnabledChanged}
|
|
256
|
+
onCIACRequestChange={setCIAC}/>
|
|
257
|
+
</>;
|
|
258
|
+
case PayoutWizardStepType.SIMPLIFIED_INPUT_LOCATION:
|
|
259
|
+
return <></>;
|
|
260
|
+
default:
|
|
261
|
+
return <></>;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return(
|
|
266
|
+
<div className="stepper-container">
|
|
267
|
+
<Box sx={{ maxWidth: 400 }}>
|
|
268
|
+
{wizardStep !== PayoutWizardStepType.DISPLAY_PAYOUTS && <Stepper activeStep={activeStep} orientation="vertical">
|
|
269
|
+
{steps.map((step, index) => (
|
|
270
|
+
<Step key={step.label}>
|
|
271
|
+
<StepLabel className={index === activeStep ? 'active' : ''} icon={steps.length == 1 ? <></> : undefined} sx={steps.length == 1 ? {pl:'5px'} : {}}>
|
|
272
|
+
{step.label}
|
|
273
|
+
{step.istoggable && <Switch checked={step.enabled[0]} onChange={(e) => {handleStepToggle(step,e.target.checked)}} inputProps={{ 'aria-label': 'controlled' }} />}
|
|
274
|
+
</StepLabel>
|
|
275
|
+
<StepContent sx={steps.length == 1 ? {ml:0,pl:'15px',pr:'20px'} : {ml:'20px',pl:'20px', pr:'20px', mr:'10px' }}>
|
|
276
|
+
<Typography>{step.description}</Typography>
|
|
277
|
+
<Box sx={{ mb: 2 }}>
|
|
278
|
+
<div>
|
|
279
|
+
{renderStepInputs(step)}
|
|
280
|
+
<Button
|
|
281
|
+
variant="contained"
|
|
282
|
+
onClick={() => handleNext(step)}
|
|
283
|
+
sx={{ mt: 1, mr: 1 }}
|
|
284
|
+
>
|
|
285
|
+
{index === steps.length - 1 ? 'Calculate Payouts' : 'Continue'}
|
|
286
|
+
</Button>
|
|
287
|
+
{steps.length > 1 && <Button
|
|
288
|
+
disabled={index === 0}
|
|
289
|
+
className='back'
|
|
290
|
+
onClick={() => handleBack(step)}
|
|
291
|
+
sx={{ mt: 1, mr: 1 }}
|
|
292
|
+
>
|
|
293
|
+
Back
|
|
294
|
+
</Button>}
|
|
295
|
+
</div>
|
|
296
|
+
</Box>
|
|
297
|
+
</StepContent>
|
|
298
|
+
</Step>
|
|
299
|
+
))}
|
|
300
|
+
</Stepper>}
|
|
301
|
+
{wizardStep === PayoutWizardStepType.DISPLAY_PAYOUTS && steps.length > 1 && (
|
|
302
|
+
<BackTestingPayoutInfo
|
|
303
|
+
mapInstanceRef={props.mapInstanceRef}
|
|
304
|
+
stormPayouts={props.stormPayouts}
|
|
305
|
+
status={props.appStatus}
|
|
306
|
+
onRestart={() => handleReset(PayoutWizardStepType.INPUT_LOCATION)}
|
|
307
|
+
onBack={() => {setWizardStep(PayoutWizardStepType.INPUT_CIAC_DETAILS)}}/>
|
|
308
|
+
)}
|
|
309
|
+
{wizardStep === PayoutWizardStepType.DISPLAY_PAYOUTS && steps.length == 1 &&(
|
|
310
|
+
<BackTestingPayoutInfo
|
|
311
|
+
mapInstanceRef={props.mapInstanceRef}
|
|
312
|
+
stormPayouts={props.stormPayouts}
|
|
313
|
+
status={props.appStatus}
|
|
314
|
+
onRestart={() => handleReset(PayoutWizardStepType.SIMPLIFIED_INPUT_LOCATION)}
|
|
315
|
+
onBack={() => {setWizardStep(PayoutWizardStepType.SIMPLIFIED_INPUT_LOCATION)}}/>
|
|
316
|
+
)}
|
|
317
|
+
</Box>
|
|
318
|
+
</div>
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export default BackTestingStepper
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { StormPayout } from "../back-testing-map/back-testing-map.service";
|
|
2
|
+
import { Payout, PayoutRequest } from "../back-testing-map/back-testing-map.types"
|
|
3
|
+
import { PayoutWizardStepType, RadioChoice, WizardStep } from "./steps/step.types"
|
|
4
|
+
|
|
5
|
+
export interface BackTestingStepperProps {
|
|
6
|
+
apiKey : string,
|
|
7
|
+
mapAccessToken : string,
|
|
8
|
+
mapInstanceRef : React.MutableRefObject<any>,
|
|
9
|
+
currentStep: PayoutWizardStepType,
|
|
10
|
+
onCurrentStepChange: (e: PayoutWizardStepType) => void,
|
|
11
|
+
payoutRequest: PayoutRequest,
|
|
12
|
+
onPayoutRequestChange: (e:PayoutRequest) => void,
|
|
13
|
+
proxyChoice: RadioChoice,
|
|
14
|
+
onProxyChoiceChange: (e:RadioChoice) => void;
|
|
15
|
+
anemometerChoice: RadioChoice,
|
|
16
|
+
onAnemometerChoiceChange: (e:RadioChoice) => void;
|
|
17
|
+
ciacChoice: RadioChoice,
|
|
18
|
+
onCIACChoiceChange: (e:RadioChoice) => void;
|
|
19
|
+
onProxyPayoutOptionChange: (e: number) => void;
|
|
20
|
+
selectedProxyPayoutOptionIndex:number;
|
|
21
|
+
proxyPayoutOptions: {key:string,value: Payout[]}[],
|
|
22
|
+
onAnemometerPayoutOptionChange: (e: number) => void;
|
|
23
|
+
selectedAnemometerPayoutOptionIndex:number;
|
|
24
|
+
anemometerPayoutOptions: {key:string,value: Payout[]}[],
|
|
25
|
+
onCIACPayoutOptionChange: (e: number) => void;
|
|
26
|
+
selectedCIACPayoutOptionIndex:number;
|
|
27
|
+
ciacPayoutOptions: {key:string,value: Payout[]}[],
|
|
28
|
+
stormPayouts: StormPayout[],
|
|
29
|
+
onStormPayoutsChanged?: (e:StormPayout[]) => void,
|
|
30
|
+
onCalculatePayoutEnabledChanged: (e:boolean) => void,
|
|
31
|
+
appStatus: 'loading'|'idle'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type PayoutWizardStep = {
|
|
35
|
+
step: PayoutWizardStepType
|
|
36
|
+
label: string
|
|
37
|
+
description: string
|
|
38
|
+
componentRef: React.RefObject<WizardStep>
|
|
39
|
+
istoggable:boolean,
|
|
40
|
+
enabled: [boolean, React.Dispatch<React.SetStateAction<boolean>>]
|
|
41
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
.distance-input-container{
|
|
2
|
+
padding: 0;
|
|
3
|
+
font-family: system-ui, sans-serif;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.distance-input-container .label{
|
|
7
|
+
font-weight: 700;
|
|
8
|
+
padding-bottom: 5px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.distance-input-container .flex-container{
|
|
12
|
+
border: 0.5px solid lightgray;
|
|
13
|
+
display:flex;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.distance-input-container .flex-container .prefix{
|
|
17
|
+
background-color: lightgray;
|
|
18
|
+
padding: 8px 15px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.distance-input-container .flex-container .distance-options #distance{
|
|
22
|
+
height: 100%;
|
|
23
|
+
outline: none;
|
|
24
|
+
box-shadow: none;
|
|
25
|
+
border: 0;
|
|
26
|
+
background-color: lightgrey;
|
|
27
|
+
padding-left: 10px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.distance-input-container .flex-container.error{
|
|
31
|
+
border: 0.5px solid rgb(211,47,47);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.distance-input-container .flex-container.error:focus-within{
|
|
35
|
+
border: 0.5px solid rgb(211,47,47) !important;
|
|
36
|
+
box-shadow: inset 1px 1px rgb(211,47,47), inset -1px -1px rgb(211,47,47);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.distance-input-container .flex-container.error:focus-within .prefix{
|
|
40
|
+
box-shadow: inset 1px 1px rgb(211,47,47), inset 0px -1px rgb(211,47,47);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.distance-input-container .flex-container:hover:not(.error:focus-within){
|
|
44
|
+
border: 0.5px solid black;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.distance-input-container input{
|
|
48
|
+
width: 100%;
|
|
49
|
+
padding: 8px 10px;
|
|
50
|
+
font-size: 1rem;
|
|
51
|
+
border: 0;
|
|
52
|
+
background-color: rgba(255,255,255,0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.distance-input-container input:focus-visible{
|
|
56
|
+
outline: 0;
|
|
57
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StoryFn, Meta } from "@storybook/react";
|
|
2
|
+
import DistanceInput from "./distance-input";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "ReactComponentLibrary/Inputs",
|
|
6
|
+
component: DistanceInput,
|
|
7
|
+
} as Meta<typeof DistanceInput>;
|
|
8
|
+
|
|
9
|
+
const Template: StoryFn<typeof DistanceInput> = (args) => <DistanceInput {...args} />;
|
|
10
|
+
|
|
11
|
+
export const DistanceInputTest = Template.bind({});
|
|
12
|
+
DistanceInputTest.args = {
|
|
13
|
+
label:"Circle radius:",
|
|
14
|
+
value:15,
|
|
15
|
+
distanceOptions:[{name:'mi',symbol:'mi'},{name:'km',symbol:'km'}],
|
|
16
|
+
error:false,
|
|
17
|
+
errorMessage:"Circle radius must be a number greater than zero",
|
|
18
|
+
step:0.1,
|
|
19
|
+
min:0
|
|
20
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import "./distance-input.css";
|
|
5
|
+
import { DistanceInputProps, DistanceUnit, kilometersToMiles, metersToMiles, milesToKilometers, milesToMeters } from "./distance-input.types";
|
|
6
|
+
|
|
7
|
+
function DistanceInput(props: DistanceInputProps){
|
|
8
|
+
|
|
9
|
+
const [milesValue, setMilesValue] = React.useState(props.value);
|
|
10
|
+
const [inputValue, setInputValue] = React.useState(props.value);
|
|
11
|
+
const [currentDistanceUnit, setCurrentDistanceUnit] = React.useState(props.unit?.symbol || props.distanceOptions[0].symbol);
|
|
12
|
+
|
|
13
|
+
const handleCurrencyChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
14
|
+
const selectedDistanceUnit = props.distanceOptions.find(option => option.name === e.target.value);
|
|
15
|
+
if (selectedDistanceUnit) {
|
|
16
|
+
if(inputValue != undefined && !Number.isNaN(inputValue as number)){
|
|
17
|
+
const convertedDistance = convertDistance(inputValue as number,currentDistanceUnit,selectedDistanceUnit.symbol)
|
|
18
|
+
setInputValue(convertedDistance);
|
|
19
|
+
}
|
|
20
|
+
setCurrentDistanceUnit(selectedDistanceUnit.symbol);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function convertDistance(dist:number,previousUnit:string,newUnit:string){
|
|
25
|
+
if(previousUnit == DistanceUnit.KILOMETER && newUnit == DistanceUnit.MILE){
|
|
26
|
+
return kilometersToMiles(dist);
|
|
27
|
+
} else if (previousUnit == DistanceUnit.MILE && newUnit == DistanceUnit.KILOMETER){
|
|
28
|
+
return milesToKilometers(dist);
|
|
29
|
+
} else if (previousUnit == DistanceUnit.KILOMETER && newUnit == DistanceUnit.METER){
|
|
30
|
+
return dist * 1000;
|
|
31
|
+
} else if (previousUnit == DistanceUnit.METER && newUnit == DistanceUnit.KILOMETER){
|
|
32
|
+
return dist / 1000;
|
|
33
|
+
} else if (previousUnit == DistanceUnit.METER && newUnit == DistanceUnit.MILE){
|
|
34
|
+
return metersToMiles(dist);
|
|
35
|
+
} else if (previousUnit == DistanceUnit.MILE && newUnit == DistanceUnit.METER){
|
|
36
|
+
return milesToMeters(dist);
|
|
37
|
+
} else {
|
|
38
|
+
return dist;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function handleChange(e: number){
|
|
43
|
+
if(Number.isNaN(e)){
|
|
44
|
+
setInputValue("")
|
|
45
|
+
if(props.onChange){
|
|
46
|
+
props.onChange(0)
|
|
47
|
+
}
|
|
48
|
+
}else{
|
|
49
|
+
setInputValue(e)
|
|
50
|
+
const convertedDistance = convertDistance(e,currentDistanceUnit,DistanceUnit.MILE)
|
|
51
|
+
if(props.onChange){
|
|
52
|
+
props.onChange(convertedDistance)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return(
|
|
58
|
+
<div className="distance-input-container">
|
|
59
|
+
{ props.label != undefined && <div className="label"><label>{props.label}</label></div>}
|
|
60
|
+
<div className={"flex-container" + (props.error ? " error" : "")}>
|
|
61
|
+
<input
|
|
62
|
+
type='number'
|
|
63
|
+
value={inputValue}
|
|
64
|
+
min={props.min}
|
|
65
|
+
max={props.max}
|
|
66
|
+
onChange={(e) => handleChange(e.target.valueAsNumber)}
|
|
67
|
+
step={props.step}/>
|
|
68
|
+
<div className='distance-options'>
|
|
69
|
+
<select
|
|
70
|
+
id='distance'
|
|
71
|
+
name='distance'
|
|
72
|
+
onChange={handleCurrencyChange}>
|
|
73
|
+
{props.distanceOptions.map(el => <option style={{borderRadius:0}} key={el.name}>{el.name}</option>)}
|
|
74
|
+
</select>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
{ props.error && <span style={{
|
|
78
|
+
color: "rgb(211,47,47)",
|
|
79
|
+
fontSize:'0.75rem',
|
|
80
|
+
margin:'0 14px',
|
|
81
|
+
marginTop:'3px',
|
|
82
|
+
display:'inline-block',
|
|
83
|
+
width:'calc(100%-28px)',
|
|
84
|
+
lineHeight: 1.66,
|
|
85
|
+
letterSpacing: '0.03333em',
|
|
86
|
+
fontFamily:'"Roboto","Helvetica","Arial",sans-serif' }}>
|
|
87
|
+
{props.errorMessage}
|
|
88
|
+
</span>}
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default DistanceInput
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const MILES_TO_METERS_FACTOR = 1609.344;
|
|
2
|
+
|
|
3
|
+
export function milesToMeters(mi:number){
|
|
4
|
+
return mi * MILES_TO_METERS_FACTOR;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function metersToMiles(m:number){
|
|
8
|
+
return m / MILES_TO_METERS_FACTOR;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function milesToKilometers(mi:number){
|
|
12
|
+
return milesToMeters(mi) / 1000;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function kilometersToMiles(km:number){
|
|
16
|
+
return 1000 * metersToMiles(km);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export enum DistanceUnit{
|
|
20
|
+
METER = 'm',
|
|
21
|
+
MILE = 'mi',
|
|
22
|
+
KILOMETER = 'km'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DistanceOption {
|
|
26
|
+
name: string;
|
|
27
|
+
symbol: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DistanceInputProps{
|
|
31
|
+
error?: boolean,
|
|
32
|
+
errorMessage?: string,
|
|
33
|
+
label?: string,
|
|
34
|
+
min?: number,
|
|
35
|
+
max?: number,
|
|
36
|
+
step?: number,
|
|
37
|
+
value?: number | string,
|
|
38
|
+
unit?: DistanceOption,
|
|
39
|
+
distanceOptions: DistanceOption[];
|
|
40
|
+
onChange?: (e: number) => void
|
|
41
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.file-input-container{
|
|
2
|
+
padding:0;
|
|
3
|
+
font-family: system-ui, sans-serif;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.file-input-container .label{
|
|
7
|
+
font-weight: 700;
|
|
8
|
+
padding-bottom: 5px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.file-input-container .file-input{
|
|
12
|
+
width:90px
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.file-input-container .file-upload {
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
position: relative;
|
|
18
|
+
left: 10px;
|
|
19
|
+
background-color: #f4f6f8;
|
|
20
|
+
font-size: 0.9em;
|
|
21
|
+
line-height: 1.6em;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.file-input-container .file-upload.selected{
|
|
25
|
+
color: green;
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StoryFn, Meta } from "@storybook/react";
|
|
2
|
+
import FileInput from "./file-input";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "ReactComponentLibrary/Inputs",
|
|
6
|
+
component: FileInput,
|
|
7
|
+
} as Meta<typeof FileInput>;
|
|
8
|
+
|
|
9
|
+
const Template: StoryFn<typeof FileInput> = (args) => <FileInput {...args} />;
|
|
10
|
+
let fileChosen = false;
|
|
11
|
+
|
|
12
|
+
export const FileInputTest = Template.bind({});
|
|
13
|
+
FileInputTest.args = {
|
|
14
|
+
error:false,
|
|
15
|
+
errorMessage:"Must upload a valid payout CSV or JSON",
|
|
16
|
+
value:undefined,
|
|
17
|
+
onChange:(() => fileChosen = true),
|
|
18
|
+
isFileSelected:fileChosen,
|
|
19
|
+
accept:".csv,.json"
|
|
20
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React, { ChangeEvent } from "react";
|
|
4
|
+
import "./file-input.css"
|
|
5
|
+
import { FileInputProps } from "./file-input.types";
|
|
6
|
+
|
|
7
|
+
function InputFile(props: FileInputProps){
|
|
8
|
+
|
|
9
|
+
const [file, setFile] = React.useState(props.value)
|
|
10
|
+
|
|
11
|
+
function handleChange(event: ChangeEvent<HTMLInputElement>) {
|
|
12
|
+
if(event.target.files != null){
|
|
13
|
+
setFile(event.target.files[0])
|
|
14
|
+
if(props.onChange != undefined){
|
|
15
|
+
props.onChange(event.target.files[0])
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return(
|
|
21
|
+
<div className={"file-input-container" + (props.error ? " error" : "")}>
|
|
22
|
+
{ props.label != undefined && <div className="label"><label>{props.label}</label></div>}
|
|
23
|
+
<label>
|
|
24
|
+
<input className="file-input" disabled={props.disabled} type="file" onChange={handleChange} accept={props.accept}></input>
|
|
25
|
+
<span className={"file-upload" + (props.isFileSelected ? " selected" : "")}>{props.isFileSelected ? 'File uploaded!' : 'No file chosen'}</span>
|
|
26
|
+
</label>
|
|
27
|
+
{ props.error && <span style={{
|
|
28
|
+
color: "rgb(211,47,47)",
|
|
29
|
+
fontSize:'0.75rem',
|
|
30
|
+
margin:'0 14px',
|
|
31
|
+
marginTop:'3px',
|
|
32
|
+
display:'inline-block',
|
|
33
|
+
width:'calc(100%-28px)',
|
|
34
|
+
lineHeight: 1.66,
|
|
35
|
+
letterSpacing: '0.03333em',
|
|
36
|
+
fontFamily:'"Roboto","Helvetica","Arial",sans-serif' }}>
|
|
37
|
+
{props.errorMessage}
|
|
38
|
+
</span>}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default InputFile
|