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.
Files changed (60) hide show
  1. package/dist/cjs/index.js +17 -17
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/esm/index.js +17 -17
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/index.d.ts +35 -10
  6. package/package.json +5 -1
  7. package/src/components/back-testing-cat-legend/back-testing-cat-legend.css +1 -1
  8. package/src/components/back-testing-cat-legend/back-testing-cat-legend.tsx +7 -7
  9. package/src/components/back-testing-map/back-testing-map.css +1 -1
  10. package/src/components/back-testing-map/back-testing-map.service.ts +21 -5
  11. package/src/components/back-testing-map/back-testing-map.tsx +51 -18
  12. package/src/components/back-testing-map/back-testing-map.types.ts +74 -11
  13. package/src/components/back-testing-payout-info/back-testing-payout-info.css +112 -0
  14. package/src/components/back-testing-payout-info/back-testing-payout-info.tsx +371 -0
  15. package/src/components/back-testing-payout-info/back-testing-payout-info.types.ts +9 -0
  16. package/src/components/back-testing-stepper/back-testing-stepper.css +30 -0
  17. package/src/components/back-testing-stepper/back-testing-stepper.tsx +322 -0
  18. package/src/components/back-testing-stepper/back-testing-stepper.types.ts +41 -0
  19. package/src/components/back-testing-stepper/inputs/distance-input/distance-input.css +57 -0
  20. package/src/components/back-testing-stepper/inputs/distance-input/distance-input.stories.tsx +20 -0
  21. package/src/components/back-testing-stepper/inputs/distance-input/distance-input.tsx +93 -0
  22. package/src/components/back-testing-stepper/inputs/distance-input/distance-input.types.ts +41 -0
  23. package/src/components/back-testing-stepper/inputs/file-input/file-input.css +26 -0
  24. package/src/components/back-testing-stepper/inputs/file-input/file-input.stories.tsx +20 -0
  25. package/src/components/back-testing-stepper/inputs/file-input/file-input.tsx +43 -0
  26. package/src/components/back-testing-stepper/inputs/file-input/file-input.types.ts +10 -0
  27. package/src/components/back-testing-stepper/inputs/number-input/number-input.css +48 -0
  28. package/src/components/back-testing-stepper/inputs/number-input/number-input.stories.tsx +20 -0
  29. package/src/components/back-testing-stepper/inputs/number-input/number-input.tsx +55 -0
  30. package/src/components/back-testing-stepper/inputs/number-input/number-input.types.ts +12 -0
  31. package/src/components/back-testing-stepper/inputs/select-input/select-input.css +13 -0
  32. package/src/components/back-testing-stepper/inputs/select-input/select-input.stories.tsx +27 -0
  33. package/src/components/back-testing-stepper/inputs/select-input/select-input.tsx +63 -0
  34. package/src/components/back-testing-stepper/inputs/select-input/select-input.types.ts +8 -0
  35. package/src/components/back-testing-stepper/inputs/text-input/text-input.css +35 -0
  36. package/src/components/back-testing-stepper/inputs/text-input/text-input.stories.tsx +18 -0
  37. package/src/components/back-testing-stepper/inputs/text-input/text-input.tsx +32 -0
  38. package/src/components/back-testing-stepper/inputs/text-input/text-input.types.ts +6 -0
  39. package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.css +16 -0
  40. package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.stories.tsx +20 -0
  41. package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.tsx +151 -0
  42. package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.types.ts +11 -0
  43. package/src/components/back-testing-stepper/steps/input-anemometer/input-anemometer.css +0 -0
  44. package/src/components/back-testing-stepper/steps/input-anemometer/input-anemometer.tsx +188 -0
  45. package/src/components/back-testing-stepper/steps/input-anemometer/input-anemometer.types.ts +16 -0
  46. package/src/components/back-testing-stepper/steps/input-ciac/input-ciac.scss +0 -0
  47. package/src/components/back-testing-stepper/steps/input-ciac/input-ciac.tsx +225 -0
  48. package/src/components/back-testing-stepper/steps/input-ciac/input-ciac.types.ts +17 -0
  49. package/src/components/back-testing-stepper/steps/input-location/input-location.css +0 -0
  50. package/src/components/back-testing-stepper/steps/input-location/input-location.tsx +135 -0
  51. package/src/components/back-testing-stepper/steps/input-location/input-location.types.ts +16 -0
  52. package/src/components/back-testing-stepper/steps/input-proxy/input-proxy.css +0 -0
  53. package/src/components/back-testing-stepper/steps/input-proxy/input-proxy.tsx +173 -0
  54. package/src/components/back-testing-stepper/steps/input-proxy/input-proxy.types.ts +16 -0
  55. package/src/components/back-testing-stepper/steps/step.types.ts +58 -0
  56. package/src/components/back-testing-storm-legend/back-testing-storm-legend.stories.tsx +56 -33
  57. package/src/components/back-testing-wizard/back-testing-wizard.css +36 -0
  58. package/src/components/back-testing-wizard/back-testing-wizard.stories.tsx +734 -0
  59. package/src/components/back-testing-wizard/back-testing-wizard.tsx +102 -0
  60. package/src/components/back-testing-wizard/back-testing-wizard.types.ts +12 -0
@@ -0,0 +1,188 @@
1
+ "use client"
2
+
3
+ import { MediaType, Payout, PayoutOption, PayoutWizardStepType, RadioChoice, WizardStep } from "../step.types";
4
+ import { LngLatLike } from "@mapbox/search-js-core";
5
+ import React, { forwardRef, useEffect, useImperativeHandle } from "react";
6
+ import { FormControl, FormControlLabel, Radio, RadioGroup } from "@mui/material";
7
+ import InputFile from "../../inputs/file-input/file-input";
8
+ import Papa from 'papaparse';
9
+ import TextInput from "../../inputs/text-input/text-input";
10
+ import SelectInput from "../../inputs/select-input/select-input";
11
+ import { AnemometerRequest, generateAnemometerRequest, PayoutRequest } from "../../../back-testing-map/back-testing-map.types";
12
+ import { InputAnemometerStepProps } from "./input-anemometer.types";
13
+ import "./input-anemometer.css"
14
+
15
+ const InputAnemometerStep = forwardRef<WizardStep, InputAnemometerStepProps>((props,ref) => {
16
+
17
+ const [payoutRequest,setPayoutRequest] = React.useState<PayoutRequest>(props.request);
18
+
19
+
20
+ const [latLng] = React.useState<LngLatLike | undefined>(payoutRequest.longitude != undefined && payoutRequest.latitude != undefined ? [payoutRequest.longitude,payoutRequest.latitude] : undefined);
21
+ const [anemometerChoice, setAnemometerChoice] = React.useState<RadioChoice>(props.anemometerChoice);
22
+
23
+ const [payoutOption, setPayoutOption] = React.useState<number>(props.selectedPayoutOptionIndex);
24
+
25
+ const [file, setFile] = React.useState<File | undefined>(undefined)
26
+ const [customPayouts, setCustomPayouts] = React.useState<Payout[] | undefined>(props.request.anemometer.payouts)
27
+
28
+ const [fileError, setFileError] = React.useState<boolean>(false);
29
+ const [fileErrorMsg, setFileErrorMsg] = React.useState<string>("");
30
+ const FILE_ERROR = "Must upload a valid payout CSV or JSON";
31
+
32
+ const [anemometerCode, setAnemometerCode] = React.useState<string>(props.request.anemometer.code)
33
+
34
+ useEffect(() => {
35
+ setPayoutRequest(props.request)
36
+ },[props.request])
37
+
38
+ function handleChoiceSelection(e: RadioChoice){
39
+ setFileError(false);
40
+ setFileErrorMsg("");
41
+ setCustomPayouts(undefined);
42
+ setFile(undefined);
43
+ setAnemometerChoice(e);
44
+ }
45
+
46
+ function saveReduxState(){
47
+ if(latLng != undefined){
48
+ let newAnemometer = undefined
49
+ if(anemometerChoice == RadioChoice.DEFAULT){
50
+ newAnemometer = generateAnemometerRequest(latLng as [number,number], anemometerCode, props.payoutOptions[props.selectedPayoutOptionIndex].value, payoutRequest.anemometer.enabled);
51
+ } else {
52
+ newAnemometer = generateAnemometerRequest(latLng as [number,number], anemometerCode, customPayouts,payoutRequest.anemometer.enabled);
53
+ }
54
+ props.onAnemometerRequestChange(newAnemometer);
55
+ props.onAnemometerChoiceChange(anemometerChoice);
56
+ props.onPayoutOptionChange(payoutOption);
57
+ }
58
+ }
59
+
60
+ async function handleFileSelection(e:File){
61
+ setFileError(false);
62
+ setFileErrorMsg("");
63
+ if(e){
64
+ let data : Payout[] = [];
65
+ if(e.type == MediaType.JSON){
66
+ readJSONFile(e).then((json) => {return json});
67
+ } else if (e.type == MediaType.CSV){
68
+ readCSVFile(e);
69
+ } else {
70
+ console.log("UNSUPPORTED file");
71
+ }
72
+ }
73
+ }
74
+
75
+ const parseJSONFile = (file: Blob) =>
76
+ new Promise<Payout[]>((resolve, reject) => {
77
+ const fileReader = new FileReader()
78
+ fileReader.onload = event => {
79
+ if (event.target) {
80
+ resolve(JSON.parse(event.target.result as string) as Payout[])
81
+ }
82
+ }
83
+ fileReader.onerror = error => reject(error)
84
+ fileReader.readAsText(file)
85
+ })
86
+
87
+ async function readJSONFile(file: Blob) {
88
+ const data = await parseJSONFile(file);
89
+ setCustomPayouts(data);
90
+ };
91
+
92
+ function readCSVFile(file: any) {
93
+ Papa.parse<Payout>(file, {
94
+ complete: (result) => {
95
+ setCustomPayouts(result.data);
96
+ },
97
+ header: true,
98
+ dynamicTyping: true
99
+ });
100
+ };
101
+
102
+ function validateStep() : boolean {
103
+ let isAnemometerChoiceError = (anemometerChoice == RadioChoice.CUSTOM && customPayouts == undefined)
104
+ if(isAnemometerChoiceError){
105
+ setFileError(true);
106
+ setFileErrorMsg(FILE_ERROR);
107
+ } else {
108
+ setFileError(false);
109
+ setFileErrorMsg("");
110
+ }
111
+ return !isAnemometerChoiceError;
112
+ }
113
+
114
+ useImperativeHandle(ref, () => ({
115
+ handleNext(){
116
+ const isValid = validateStep();
117
+ if(isValid){
118
+ if(latLng != undefined){
119
+ saveReduxState();
120
+ props.onStepChange(PayoutWizardStepType.INPUT_CIAC_DETAILS);
121
+ props.onActiveStepChange(props.activeStep + 1)
122
+ }
123
+ }
124
+ },
125
+
126
+ handleBack(){
127
+ if(latLng != undefined){
128
+ saveReduxState();
129
+ props.onStepChange(PayoutWizardStepType.INPUT_PROXY_DETAILS);
130
+ props.onActiveStepChange(props.activeStep - 1)
131
+ }
132
+ }
133
+ }));
134
+
135
+ function updatePayoutOption(e:PayoutOption){
136
+ setPayoutOption(e);
137
+ }
138
+
139
+ return (
140
+ <div>
141
+ <div className="ptop-10 pbot-10">
142
+ <TextInput
143
+ label="Anemometer Code:"
144
+ value={anemometerCode}
145
+ onChange={(e) => setAnemometerCode(e)} />
146
+ </div>
147
+ <FormControl sx={{width:'100%'}}>
148
+ <label className="ptop-20 bold">Select a payout table:</label>
149
+ <RadioGroup
150
+ aria-labelledby="demo-radio-buttons-group-label"
151
+ sx={{width:'100%'}}
152
+ value={anemometerChoice}
153
+ onChange={(e) => handleChoiceSelection((e.target as HTMLInputElement).value as RadioChoice)}
154
+ name="radio-buttons-group">
155
+ <FormControlLabel
156
+ value={RadioChoice.DEFAULT}
157
+ control={<Radio />}
158
+ label="Preconfigured Table" />
159
+ <div className="">
160
+ <SelectInput
161
+ disabled={anemometerChoice != RadioChoice.DEFAULT}
162
+ onLoad={(e:number) => updatePayoutOption(e)}
163
+ onChange={(e:number) => updatePayoutOption(e)}
164
+ defaultValue={payoutOption}
165
+ items={props.payoutOptions.map((option, index) => {return {key:option.key, value:index}})}>
166
+ </SelectInput>
167
+ </div>
168
+ <FormControlLabel
169
+ value={RadioChoice.CUSTOM}
170
+ control={<Radio />}
171
+ label="Upload table" />
172
+ </RadioGroup>
173
+ </FormControl>
174
+ <div className="ptop-10 pbot-10">
175
+ <InputFile
176
+ disabled={anemometerChoice != RadioChoice.CUSTOM}
177
+ error={fileError}
178
+ errorMessage={fileErrorMsg}
179
+ value={file}
180
+ onChange={handleFileSelection}
181
+ isFileSelected={anemometerChoice == RadioChoice.CUSTOM && customPayouts != undefined}
182
+ accept=".csv,.json"/>
183
+ </div>
184
+ </div>
185
+ );
186
+ })
187
+
188
+ export default InputAnemometerStep
@@ -0,0 +1,16 @@
1
+ import { AnemometerRequest, PayoutRequest } from "../../../back-testing-map/back-testing-map.types";
2
+ import { Payout, PayoutWizardStepType, RadioChoice } from "../step.types";
3
+
4
+ export interface InputAnemometerStepProps {
5
+ mapInstanceRef: React.MutableRefObject<any>,
6
+ activeStep: number,
7
+ request: PayoutRequest,
8
+ selectedPayoutOptionIndex: number,
9
+ payoutOptions: {key:string,value: Payout[]}[],
10
+ anemometerChoice: RadioChoice,
11
+ onActiveStepChange: (e: number) => void,
12
+ onStepChange: (e: PayoutWizardStepType) => void,
13
+ onAnemometerChoiceChange: (e:RadioChoice) => void,
14
+ onPayoutOptionChange: (e:number) => void,
15
+ onAnemometerRequestChange: (e:AnemometerRequest) => void
16
+ }
@@ -0,0 +1,225 @@
1
+ "use client"
2
+
3
+ import { MediaType, Payout, PayoutOption, PayoutWizardStepType, RadioChoice, WizardStep } from "../step.types";
4
+ import { LngLatLike } from "@mapbox/search-js-core";
5
+ import React, { forwardRef, useEffect, useImperativeHandle } from "react";
6
+ import { FormControl, FormControlLabel, Radio, RadioGroup } from "@mui/material";
7
+ import InputFile from "../../inputs/file-input/file-input";
8
+ import { AnemometerRequest, CIAC_LAYER, CIACRequest, generateCIACRequest, PayoutRequest, ProxyRequest } from "../../../back-testing-map/back-testing-map.types";
9
+ import * as turf from "@turf/turf";
10
+ import { GeoJSONSource } from "mapbox-gl";
11
+ import Papa from 'papaparse';
12
+ import DistanceInput from "../../inputs/distance-input/distance-input";
13
+ import SelectInput from "../../inputs/select-input/select-input";
14
+ import { InputCIACStepProps } from "./input-ciac.types";
15
+
16
+ const InputCIACStep = forwardRef<WizardStep, InputCIACStepProps>((props,ref) => {
17
+
18
+ const [payoutRequest, setPayoutRequest] = React.useState<PayoutRequest>(props.request);
19
+
20
+ const [latLng] = React.useState<LngLatLike | undefined>(payoutRequest.longitude != undefined && payoutRequest.latitude != undefined ? [payoutRequest.longitude,payoutRequest.latitude] : undefined);
21
+ const [ciacRadius, setCIACRadius] = React.useState<number | undefined>(props.request.ciac.shapes[0].radius);
22
+ const [ciacChoice, setCIACChoice] = React.useState(props.ciacChoice);
23
+
24
+ const [file, setFile] = React.useState<File | undefined>(undefined)
25
+ const [customPayouts, setCustomPayouts] = React.useState<Payout[] | undefined>(props.request.ciac.payouts);
26
+
27
+ const [payoutOption, setPayoutOption] = React.useState<number>(props.selectedPayoutOptionIndex);
28
+
29
+ const [fileError, setFileError] = React.useState<boolean>(false);
30
+ const [fileErrorMsg, setFileErrorMsg] = React.useState<string>("");
31
+ const FILE_ERROR = "Must upload a valid payout CSV or JSON";
32
+
33
+ const [ciacError, setCIACError] = React.useState<boolean>(false);
34
+ const [ciacErrorMsg, setCIACErrorMsg] = React.useState<string>("");
35
+ const CIAC_RADIUS_ERROR = "CAT circle radius must be >= 0";
36
+
37
+ function handleCIACRadiusChange(e:number){
38
+ setCIACError(false);
39
+ setCIACErrorMsg("");
40
+ if(e != undefined && !Number.isNaN(e)){
41
+ setCIACRadius(e)
42
+ }
43
+ }
44
+
45
+ useEffect(() => {
46
+ setPayoutRequest(props.request)
47
+ },[props.request])
48
+
49
+ useEffect(() => {
50
+ if(ciacRadius != undefined){
51
+ props.onCIACRequestChange(updateCIAC());
52
+ }
53
+ },[ciacRadius])
54
+
55
+ function handleChoiceSelection(e: RadioChoice){
56
+ setFileError(false);
57
+ setFileErrorMsg("");
58
+ setCustomPayouts(undefined);
59
+ setFile(undefined);
60
+ setCIACChoice(e);
61
+ }
62
+
63
+ function saveState(){
64
+ if(latLng != undefined){
65
+ let newCIAC = updateCIAC();
66
+ props.onCIACRequestChange(newCIAC);
67
+ props.onCIACChoiceChange(ciacChoice);
68
+ props.onPayoutOptionChange(payoutOption);
69
+ }
70
+ }
71
+
72
+ function updateCIAC(){
73
+ let newCIAC = undefined
74
+ if(ciacChoice == RadioChoice.DEFAULT){
75
+ newCIAC = generateCIACRequest(latLng as [number,number], ciacRadius, false, props.payoutOptions[props.selectedPayoutOptionIndex].value,payoutRequest.ciac.enabled);
76
+ } else {
77
+ newCIAC = generateCIACRequest(latLng as [number,number], ciacRadius, false, customPayouts,payoutRequest.ciac.enabled);
78
+ }
79
+ return newCIAC;
80
+ }
81
+
82
+ async function handleFileSelection(e:File){
83
+ setFileError(false);
84
+ setFileErrorMsg("");
85
+ if(e){
86
+ if(e.type == MediaType.JSON){
87
+ readJSONFile(e).then((json) => {return json});
88
+ } else if (e.type == MediaType.CSV){
89
+ readCSVFile(e);
90
+ } else {
91
+ console.log("UNSUPPORTED file");
92
+ }
93
+ }
94
+ }
95
+
96
+ const parseJSONFile = (file: Blob) =>
97
+ new Promise<Payout[]>((resolve, reject) => {
98
+ const fileReader = new FileReader()
99
+ fileReader.onload = event => {
100
+ if (event.target) {
101
+ resolve(JSON.parse(event.target.result as string) as Payout[])
102
+ }
103
+ }
104
+ fileReader.onerror = error => reject(error)
105
+ fileReader.readAsText(file)
106
+ })
107
+
108
+ async function readJSONFile(file: Blob) {
109
+ const data = await parseJSONFile(file);
110
+ setCustomPayouts(data);
111
+ };
112
+
113
+ function readCSVFile(file: any) {
114
+ Papa.parse<Payout>(file, {
115
+ complete: (result) => {
116
+ setCustomPayouts(result.data);
117
+ },
118
+ header: true,
119
+ dynamicTyping: true
120
+ });
121
+ };
122
+
123
+ function validateStep() : boolean {
124
+ let isCIACChoiceError = (ciacChoice == RadioChoice.CUSTOM && customPayouts == undefined)
125
+ if(isCIACChoiceError){
126
+ setFileError(true);
127
+ setFileErrorMsg(FILE_ERROR);
128
+ }else {
129
+ setFileError(false);
130
+ setFileErrorMsg("");
131
+ }
132
+
133
+ let isCIACError = !Number.isSafeInteger(ciacRadius) || ciacRadius == undefined || ciacRadius < 0;
134
+ if(isCIACError){
135
+ setCIACError(true)
136
+ setCIACErrorMsg(CIAC_RADIUS_ERROR);
137
+ } else {
138
+ setCIACError(false)
139
+ setCIACErrorMsg("");
140
+ }
141
+ return !isCIACError && !isCIACChoiceError;
142
+ }
143
+
144
+ useImperativeHandle(ref, () => ({
145
+ handleNext(){
146
+ const isValid = validateStep();
147
+ if(isValid){
148
+ if(latLng != undefined){
149
+ saveState();
150
+ props.onStepChange(PayoutWizardStepType.DISPLAY_PAYOUTS);
151
+ props.onCalculatePayoutEnabledChanged(true);
152
+ }
153
+ }
154
+ },
155
+
156
+ handleBack(){
157
+ if(latLng != undefined){
158
+ saveState();
159
+ props.onStepChange(PayoutWizardStepType.INPUT_ANEMOMETER_DETAILS);
160
+ props.onActiveStepChange(props.activeStep - 1)
161
+ }
162
+ }
163
+ }));
164
+
165
+ function updatePayoutOption(e:PayoutOption){
166
+ setPayoutOption(e);
167
+ }
168
+
169
+ return (
170
+ <div>
171
+ <div className="ptop-10 pbot-10">
172
+ <DistanceInput
173
+ label="Circle radius:"
174
+ value={ciacRadius}
175
+ distanceOptions={[{name:'mi',symbol:'mi'},{name:'km',symbol:'km'}]}
176
+ error={ciacError}
177
+ errorMessage={ciacErrorMsg}
178
+ onChange={(e) => handleCIACRadiusChange(e)}
179
+ step={0.1}
180
+ min={0} />
181
+ </div>
182
+ <FormControl sx={{width:'100%'}}>
183
+ <label className="ptop-20 bold">Select a payout table:</label>
184
+ <RadioGroup
185
+ aria-labelledby="demo-radio-buttons-group-label"
186
+ sx={{width:'100%'}}
187
+ value={ciacChoice}
188
+ onChange={(e) => handleChoiceSelection((e.target as HTMLInputElement).value as RadioChoice)}
189
+ name="radio-buttons-group">
190
+ <FormControlLabel
191
+ value={RadioChoice.DEFAULT}
192
+ sx={{pt:0}}
193
+ control={<Radio />}
194
+ label="Preconfigured Table" />
195
+ <div className="">
196
+ <SelectInput
197
+ disabled={ciacChoice != RadioChoice.DEFAULT}
198
+ onLoad={(e:number) => updatePayoutOption(e)}
199
+ onChange={(e:number) => updatePayoutOption(e)}
200
+ defaultValue={payoutOption}
201
+ items={props.payoutOptions.map((option, index) => {return {key:option.key, value:index}})}>
202
+ </SelectInput>
203
+ </div>
204
+ <FormControlLabel
205
+ value={RadioChoice.CUSTOM}
206
+ control={<Radio />}
207
+ label="Upload table" />
208
+ </RadioGroup>
209
+ </FormControl>
210
+ <div className="ptop-10 pbot-10">
211
+ <InputFile
212
+ disabled={ciacChoice != RadioChoice.CUSTOM}
213
+ value={file}
214
+ error={fileError}
215
+ errorMessage={fileErrorMsg}
216
+ onChange={handleFileSelection}
217
+ isFileSelected={ciacChoice == RadioChoice.CUSTOM && customPayouts != undefined}
218
+ accept=".csv,.json"/>
219
+ </div>
220
+ <div className="ptop-10 pbot-10"/>
221
+ </div>
222
+ );
223
+ })
224
+
225
+ export default InputCIACStep
@@ -0,0 +1,17 @@
1
+ import { AnemometerRequest, CIACRequest, PayoutRequest, ProxyRequest } from "../../../back-testing-map/back-testing-map.types";
2
+ import { Payout, PayoutWizardStepType, RadioChoice } from "../step.types";
3
+
4
+ export interface InputCIACStepProps {
5
+ mapInstanceRef: React.MutableRefObject<any>,
6
+ activeStep: number,
7
+ request: PayoutRequest,
8
+ selectedPayoutOptionIndex: number,
9
+ payoutOptions: {key:string,value: Payout[]}[],
10
+ ciacChoice: RadioChoice,
11
+ onActiveStepChange: (e: number) => void,
12
+ onStepChange: (e: PayoutWizardStepType) => void,
13
+ onCIACChoiceChange: (e:RadioChoice) => void,
14
+ onPayoutOptionChange: (e:number) => void,
15
+ onCIACRequestChange: (e:CIACRequest) => void,
16
+ onCalculatePayoutEnabledChanged: (e:boolean) => void,
17
+ }
@@ -0,0 +1,135 @@
1
+ "use client"
2
+
3
+ import NumberInput from "../../inputs/number-input/number-input";
4
+ import WorldMapSearch from '../../inputs/worldmap-search/worldmap-search'
5
+ import { PayoutWizardStepType, WizardStep } from "../step.types";
6
+ import { LngLatLike, SearchBoxRetrieveResponse } from "@mapbox/search-js-core";
7
+ import React, { forwardRef, useImperativeHandle } from "react";
8
+ import { InputLocationStepProps } from "./input-location.types";
9
+ import { generateProxyRequest, PayoutRequest } from "../../../back-testing-map/back-testing-map.types";
10
+
11
+ const InputLocationStep = forwardRef<WizardStep, InputLocationStepProps>((props,ref) => {
12
+
13
+ const [request] = React.useState<PayoutRequest>(props.payoutRequest);
14
+
15
+ const [locationMarker, setLocationMarker] = React.useState<mapboxgl.Marker>(props.marker);
16
+ const [latLng, setLatLng] = React.useState<LngLatLike | undefined>(request.longitude != undefined && request.latitude != undefined ? [request.longitude,request.latitude] : undefined);
17
+ const [locationString, setLocationString] = React.useState<string>(props.address);
18
+ const [insuranceLimit, setInsuranceLimit] = React.useState<number>(request.limit);
19
+
20
+ const [addressError, setAddressError] = React.useState<boolean>(false);
21
+ const [addressErrorMsg, setAddressErrorMsg] = React.useState<string>("");
22
+ const ADDRESS_ERROR = "Must input address and select option from dropdown";
23
+
24
+ const [limitError, setLimitError] = React.useState<boolean>(false);
25
+ const [limitErrorMsg, setLimitErrorMsg] = React.useState<string>("");
26
+ const LIMIT_ERROR = "Insurance limit must be a positive integer";
27
+
28
+ function changeLocationString(value:string){
29
+ setAddressError(false);
30
+ setAddressErrorMsg("");
31
+ setLocationString(value);
32
+ }
33
+
34
+ function changeInsuranceLimit(value:number){
35
+ setLimitError(false);
36
+ setLimitErrorMsg("");
37
+ setInsuranceLimit(value);
38
+ }
39
+
40
+ function setLocationLatLng(resp: SearchBoxRetrieveResponse){
41
+ let latLng : LngLatLike = [resp.features[0].geometry.coordinates.at(0) as number,resp.features[0].geometry.coordinates.at(1) as number];
42
+ if(props.mapInstanceRef != undefined && latLng != undefined){
43
+ setLocationMarker(locationMarker.remove().setLngLat(latLng).addTo(props.mapInstanceRef.current));
44
+ props.mapInstanceRef.current.flyTo({center:
45
+ latLng,
46
+ essential: true});
47
+ setAddressError(false);
48
+ setAddressErrorMsg("");
49
+ }
50
+ setLatLng(latLng);
51
+ }
52
+
53
+ function saveState(){
54
+ if(latLng != undefined){
55
+ let coord = (latLng as [number,number]);
56
+ let newProxy = generateProxyRequest(latLng as [number,number], 1000, request.proxy.payouts,request.proxy.enabled);
57
+ let newRequest : PayoutRequest = {
58
+ includeZeroPayouts: request.includeZeroPayouts,
59
+ limit: insuranceLimit,
60
+ latitude: coord[1],
61
+ longitude: coord[0],
62
+ proxy: newProxy,
63
+ anemometer: request.anemometer,
64
+ ciac: request.ciac
65
+ }
66
+ props.onLocationAddressChange(locationString);
67
+ props.onPayoutRequestChange(newRequest);
68
+ }
69
+ }
70
+
71
+ function validateStep() : boolean {
72
+ // validate address input
73
+ let addressError = locationString.trim().length == 0 || latLng == undefined || locationMarker._map == undefined;
74
+ if(addressError){
75
+ setAddressError(true)
76
+ setAddressErrorMsg(ADDRESS_ERROR);
77
+ } else{
78
+ setAddressError(false);
79
+ setAddressErrorMsg("");
80
+ }
81
+
82
+ let limitError = !Number.isSafeInteger(insuranceLimit) || insuranceLimit <= 0;
83
+ if(limitError){
84
+ setLimitError(true)
85
+ setLimitErrorMsg(LIMIT_ERROR);
86
+ } else{
87
+ setLimitError(false);
88
+ setLimitErrorMsg("");
89
+ }
90
+ return addressError || limitError;
91
+ }
92
+
93
+ useImperativeHandle(ref, () => ({
94
+ handleNext(){
95
+ let valid = !validateStep()
96
+ if(valid){
97
+ if(latLng != undefined){
98
+ saveState();
99
+ props.onStepChange(PayoutWizardStepType.INPUT_PROXY_DETAILS);
100
+ props.onActiveStepChange(props.activeStep + 1)
101
+ }
102
+ }
103
+ },
104
+
105
+ handleBack(){}
106
+ }));
107
+
108
+ return (
109
+ <div>
110
+ <div className="ptop-10 pbot-20">
111
+ <WorldMapSearch
112
+ value={locationString}
113
+ error={addressError}
114
+ errorMessage={addressErrorMsg}
115
+ label="Risk Location Address:"
116
+ accessToken={props.accessToken}
117
+ onChange={(e) => changeLocationString(e)}
118
+ onLocationRetrieved={(e) => setLocationLatLng(e)}/>
119
+ </div>
120
+ <div className="pbot-10">
121
+ <NumberInput
122
+ label="Limit of Insurance:"
123
+ value={insuranceLimit}
124
+ error={limitError}
125
+ errorMessage={limitErrorMsg}
126
+ onChange={(e) => changeInsuranceLimit(e as number)}
127
+ prefix="$"
128
+ step={1}
129
+ min={0}/>
130
+ </div>
131
+ </div>
132
+ );
133
+ })
134
+
135
+ export default InputLocationStep
@@ -0,0 +1,16 @@
1
+ import { LngLatLike } from "@mapbox/search-js-core";
2
+ import { PayoutWizardStepType } from "../step.types";
3
+ import { PayoutRequest, ProxyRequest } from "../../../back-testing-map/back-testing-map.types";
4
+
5
+ export interface InputLocationStepProps {
6
+ accessToken: string,
7
+ mapInstanceRef: React.MutableRefObject<any>,
8
+ marker: mapboxgl.Marker,
9
+ activeStep: number,
10
+ payoutRequest: PayoutRequest,
11
+ address: string,
12
+ onActiveStepChange: (e: number) => void,
13
+ onStepChange: (e: PayoutWizardStepType) => void,
14
+ onPayoutRequestChange: (e: PayoutRequest) => void,
15
+ onLocationAddressChange: (e: string) => void
16
+ }