@controleonline/ui-default 1.0.263 → 1.0.264
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/AGENTS.md +48 -47
- package/package.json +3 -6
- package/src/react/components/errors/DefaultErrors.js +360 -360
- package/src/react/components/files/DefaultFile.js +46 -46
- package/src/react/components/filters/CompactFilterSelector.js +22 -3
- package/src/react/components/filters/DefaultColumnFilter.js +15 -13
- package/src/react/components/filters/DefaultExternalFilters.js +44 -39
- package/src/react/components/filters/DefaultSearch.js +140 -103
- package/src/react/components/filters/DefaultSearch.styles.js +65 -70
- package/src/react/components/form/DefaultForm.js +200 -198
- package/src/react/components/help/DefaultTooltip.js +87 -87
- package/src/react/components/help/DefaultTooltip.styles.js +61 -61
- package/src/react/components/inputs/DefaultInput.js +162 -160
- package/src/react/components/inputs/DefaultSelect.js +203 -192
- package/src/react/components/map/DefaultGoogleMap.styles.js +115 -9
- package/src/react/components/map/DefaultGoogleMap.web.js +560 -446
- package/src/react/components/map/DefaultMap.native.js +73 -71
- package/src/react/components/map/DefaultMap.styles.js +16 -16
- package/src/react/components/map/DefaultMap.web.js +68 -66
- package/src/react/components/map/DefaultNativeMap.native.js +307 -183
- package/src/react/components/map/DefaultNativeMap.shared.js +439 -340
- package/src/react/components/map/DefaultNativeMap.styles.js +156 -122
- package/src/react/components/table/DefaultTable.js +529 -355
- package/src/react/components/table/DefaultTable.styles.js +30 -39
- package/src/react/index.js +10 -10
- package/src/tests/react/components/errors/DefaultErrors.test.js +162 -162
- package/src/tests/react/components/files/DefaultFile.test.js +80 -80
- package/src/tests/react/components/inputs/DefaultSelect.test.js +93 -0
- package/src/tests/react/components/table/DefaultTable.test.js +334 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
|
2
|
-
import {ActivityIndicator, Image, Linking, Platform, Text, View} from 'react-native';
|
|
3
|
-
import {env} from '@env';
|
|
4
|
-
|
|
5
|
-
import styles from './DefaultNativeMap.styles';
|
|
6
|
-
import {
|
|
7
|
-
buildGoogleMapsNavigationUrl,
|
|
8
|
-
buildWazeNavigationUrl,
|
|
9
|
-
buildAndroidWebMapHtml,
|
|
10
|
-
resolveWebViewBaseUrlForDomain,
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
|
2
|
+
import {ActivityIndicator, Image, Linking, Platform, Text, View} from 'react-native';
|
|
3
|
+
import {env} from '@env';
|
|
4
|
+
|
|
5
|
+
import styles from './DefaultNativeMap.styles';
|
|
6
|
+
import {
|
|
7
|
+
buildGoogleMapsNavigationUrl,
|
|
8
|
+
buildWazeNavigationUrl,
|
|
9
|
+
buildAndroidWebMapHtml,
|
|
10
|
+
resolveWebViewBaseUrlForDomain,
|
|
11
|
+
resolveMapPopupTheme,
|
|
12
|
+
} from './DefaultNativeMap.shared';
|
|
13
|
+
import {extractMapCoordinates} from './DefaultMap.shared';
|
|
13
14
|
|
|
14
15
|
const getNativeMapComponents = () => {
|
|
15
16
|
if (Platform.OS === 'android') {
|
|
@@ -102,7 +103,7 @@ const decodePolyline = encoded => {
|
|
|
102
103
|
return coordinates;
|
|
103
104
|
};
|
|
104
105
|
|
|
105
|
-
const
|
|
106
|
+
const fetchRouteData = async ({apiKey, origin, destination}) => {
|
|
106
107
|
if (
|
|
107
108
|
!apiKey ||
|
|
108
109
|
!Number.isFinite(origin?.latitude) ||
|
|
@@ -110,7 +111,11 @@ const fetchRouteCoordinates = async ({apiKey, origin, destination}) => {
|
|
|
110
111
|
!Number.isFinite(destination?.latitude) ||
|
|
111
112
|
!Number.isFinite(destination?.longitude)
|
|
112
113
|
) {
|
|
113
|
-
return
|
|
114
|
+
return {
|
|
115
|
+
coordinates: [],
|
|
116
|
+
distanceLabel: '',
|
|
117
|
+
durationLabel: '',
|
|
118
|
+
};
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
const response = await fetch(
|
|
@@ -122,12 +127,21 @@ const fetchRouteCoordinates = async ({apiKey, origin, destination}) => {
|
|
|
122
127
|
);
|
|
123
128
|
const payload = await response.json();
|
|
124
129
|
const encodedPoints = payload?.routes?.[0]?.overview_polyline?.points;
|
|
130
|
+
const activeLeg = payload?.routes?.[0]?.legs?.[0] || null;
|
|
125
131
|
|
|
126
132
|
if (payload?.status !== 'OK' || !encodedPoints) {
|
|
127
|
-
return
|
|
133
|
+
return {
|
|
134
|
+
coordinates: [],
|
|
135
|
+
distanceLabel: '',
|
|
136
|
+
durationLabel: '',
|
|
137
|
+
};
|
|
128
138
|
}
|
|
129
139
|
|
|
130
|
-
return
|
|
140
|
+
return {
|
|
141
|
+
coordinates: decodePolyline(encodedPoints),
|
|
142
|
+
distanceLabel: String(activeLeg?.distance?.text || '').trim(),
|
|
143
|
+
durationLabel: String(activeLeg?.duration?.text || '').trim(),
|
|
144
|
+
};
|
|
131
145
|
};
|
|
132
146
|
|
|
133
147
|
const buildRegion = coordinates => {
|
|
@@ -159,20 +173,24 @@ const buildRegion = coordinates => {
|
|
|
159
173
|
};
|
|
160
174
|
};
|
|
161
175
|
|
|
162
|
-
const MetaRow = ({label, value}) => {
|
|
176
|
+
const MetaRow = ({label, value, popupTheme}) => {
|
|
163
177
|
if (!value) {
|
|
164
178
|
return null;
|
|
165
179
|
}
|
|
166
180
|
|
|
167
181
|
return (
|
|
168
182
|
<View style={styles.metaRow}>
|
|
169
|
-
<Text style={styles.metaLabel
|
|
170
|
-
|
|
183
|
+
<Text style={[styles.metaLabel, popupTheme && {color: popupTheme.textMuted}]}>
|
|
184
|
+
{label}
|
|
185
|
+
</Text>
|
|
186
|
+
<Text style={[styles.metaValue, popupTheme && {color: popupTheme.modalText}]}>
|
|
187
|
+
{value}
|
|
188
|
+
</Text>
|
|
171
189
|
</View>
|
|
172
190
|
);
|
|
173
191
|
};
|
|
174
192
|
|
|
175
|
-
const CalloutAction = ({label, onPress, primary = false}) => {
|
|
193
|
+
const CalloutAction = ({label, onPress, popupTheme, primary = false}) => {
|
|
176
194
|
if (!CalloutSubview || !label || typeof onPress !== 'function') {
|
|
177
195
|
return null;
|
|
178
196
|
}
|
|
@@ -180,47 +198,73 @@ const CalloutAction = ({label, onPress, primary = false}) => {
|
|
|
180
198
|
return (
|
|
181
199
|
<CalloutSubview
|
|
182
200
|
onPress={onPress}
|
|
183
|
-
style={[
|
|
184
|
-
|
|
201
|
+
style={[
|
|
202
|
+
styles.actionButton,
|
|
203
|
+
primary && styles.actionButtonPrimary,
|
|
204
|
+
popupTheme && {
|
|
205
|
+
borderColor: popupTheme.buttonBackground,
|
|
206
|
+
backgroundColor: popupTheme.buttonBackground,
|
|
207
|
+
},
|
|
208
|
+
]}>
|
|
209
|
+
<Text
|
|
210
|
+
style={[
|
|
211
|
+
styles.actionText,
|
|
212
|
+
primary && styles.actionTextPrimary,
|
|
213
|
+
popupTheme && {color: popupTheme.buttonText},
|
|
214
|
+
]}>
|
|
185
215
|
{label}
|
|
186
216
|
</Text>
|
|
187
217
|
</CalloutSubview>
|
|
188
|
-
);
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const buildTravelSummary = (item, routeSummary = null) => {
|
|
222
|
+
const distanceLabel =
|
|
223
|
+
routeSummary?.distanceLabel || item?.distanceLabel || '';
|
|
224
|
+
const durationLabel =
|
|
225
|
+
routeSummary?.durationLabel ||
|
|
226
|
+
item?.durationLabel ||
|
|
227
|
+
item?.travelDurationLabel ||
|
|
228
|
+
'';
|
|
229
|
+
|
|
230
|
+
return [distanceLabel, durationLabel].filter(Boolean).join(' • ');
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const resolveMarkerNavigationUrls = item => {
|
|
234
|
+
const position = {
|
|
235
|
+
latitude: Number(item?.latitude),
|
|
236
|
+
longitude: Number(item?.longitude),
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
googleMapsUrl:
|
|
241
|
+
item?.googleMapsUrl ||
|
|
242
|
+
buildGoogleMapsNavigationUrl(position),
|
|
243
|
+
wazeUrl:
|
|
244
|
+
item?.wazeUrl ||
|
|
245
|
+
buildWazeNavigationUrl(position),
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export default function DefaultNativeMap({
|
|
250
|
+
apiKey = '',
|
|
251
|
+
markerPayloads = [],
|
|
252
|
+
paths = [],
|
|
253
|
+
routeColor = '#0EA5E9',
|
|
254
|
+
userCoordinates = null,
|
|
255
|
+
popupTheme = null,
|
|
256
|
+
}) {
|
|
214
257
|
const mapRef = useRef(null);
|
|
215
258
|
const [mapReady, setMapReady] = useState(false);
|
|
216
259
|
const [selectedMarkerId, setSelectedMarkerId] = useState(null);
|
|
217
260
|
const [routeCoordinates, setRouteCoordinates] = useState([]);
|
|
261
|
+
const [selectedRouteSummary, setSelectedRouteSummary] = useState(null);
|
|
218
262
|
const [androidMapState, setAndroidMapState] = useState('loading');
|
|
219
263
|
const [androidMapErrorMessage, setAndroidMapErrorMessage] = useState('');
|
|
220
264
|
const hasUserCoordinates =
|
|
221
265
|
Number.isFinite(userCoordinates?.latitude) &&
|
|
222
266
|
Number.isFinite(userCoordinates?.longitude);
|
|
223
|
-
const baseMapCoordinates = useMemo(() => {
|
|
267
|
+
const baseMapCoordinates = useMemo(() => {
|
|
224
268
|
const coordinates = markerPayloads
|
|
225
269
|
.map(item => ({
|
|
226
270
|
latitude: Number(item?.latitude),
|
|
@@ -237,28 +281,28 @@ export default function DefaultNativeMap({
|
|
|
237
281
|
});
|
|
238
282
|
}
|
|
239
283
|
|
|
240
|
-
return coordinates;
|
|
241
|
-
}, [hasUserCoordinates, markerPayloads, userCoordinates]);
|
|
242
|
-
const pathCoordinates = useMemo(
|
|
243
|
-
() =>
|
|
244
|
-
(Array.isArray(paths) ? paths : []).flatMap(path => {
|
|
245
|
-
const from = extractMapCoordinates(path?.from || path?.origin || path?.start);
|
|
246
|
-
const to = extractMapCoordinates(path?.to || path?.destination || path?.end);
|
|
247
|
-
|
|
248
|
-
return [from, to].filter(Boolean);
|
|
249
|
-
}),
|
|
250
|
-
[paths],
|
|
251
|
-
);
|
|
252
|
-
const selectedMarker = useMemo(
|
|
253
|
-
() => markerPayloads.find(item => item.id === selectedMarkerId) || null,
|
|
254
|
-
[markerPayloads, selectedMarkerId],
|
|
255
|
-
);
|
|
256
|
-
const focusCoordinates = useMemo(() => {
|
|
257
|
-
const combinedCoordinates = [...baseMapCoordinates, ...pathCoordinates];
|
|
258
|
-
|
|
259
|
-
if (routeCoordinates.length > 1) {
|
|
260
|
-
return routeCoordinates;
|
|
261
|
-
}
|
|
284
|
+
return coordinates;
|
|
285
|
+
}, [hasUserCoordinates, markerPayloads, userCoordinates]);
|
|
286
|
+
const pathCoordinates = useMemo(
|
|
287
|
+
() =>
|
|
288
|
+
(Array.isArray(paths) ? paths : []).flatMap(path => {
|
|
289
|
+
const from = extractMapCoordinates(path?.from || path?.origin || path?.start);
|
|
290
|
+
const to = extractMapCoordinates(path?.to || path?.destination || path?.end);
|
|
291
|
+
|
|
292
|
+
return [from, to].filter(Boolean);
|
|
293
|
+
}),
|
|
294
|
+
[paths],
|
|
295
|
+
);
|
|
296
|
+
const selectedMarker = useMemo(
|
|
297
|
+
() => markerPayloads.find(item => item.id === selectedMarkerId) || null,
|
|
298
|
+
[markerPayloads, selectedMarkerId],
|
|
299
|
+
);
|
|
300
|
+
const focusCoordinates = useMemo(() => {
|
|
301
|
+
const combinedCoordinates = [...baseMapCoordinates, ...pathCoordinates];
|
|
302
|
+
|
|
303
|
+
if (routeCoordinates.length > 1) {
|
|
304
|
+
return routeCoordinates;
|
|
305
|
+
}
|
|
262
306
|
|
|
263
307
|
if (
|
|
264
308
|
hasUserCoordinates &&
|
|
@@ -276,44 +320,51 @@ export default function DefaultNativeMap({
|
|
|
276
320
|
},
|
|
277
321
|
];
|
|
278
322
|
}
|
|
279
|
-
|
|
280
|
-
return combinedCoordinates;
|
|
281
|
-
}, [
|
|
282
|
-
baseMapCoordinates,
|
|
283
|
-
hasUserCoordinates,
|
|
284
|
-
routeCoordinates,
|
|
285
|
-
pathCoordinates,
|
|
286
|
-
selectedMarker?.latitude,
|
|
287
|
-
selectedMarker?.longitude,
|
|
288
|
-
userCoordinates,
|
|
289
|
-
]);
|
|
323
|
+
|
|
324
|
+
return combinedCoordinates;
|
|
325
|
+
}, [
|
|
326
|
+
baseMapCoordinates,
|
|
327
|
+
hasUserCoordinates,
|
|
328
|
+
routeCoordinates,
|
|
329
|
+
pathCoordinates,
|
|
330
|
+
selectedMarker?.latitude,
|
|
331
|
+
selectedMarker?.longitude,
|
|
332
|
+
userCoordinates,
|
|
333
|
+
]);
|
|
290
334
|
|
|
291
335
|
const initialRegion = useMemo(() => buildRegion(focusCoordinates), [focusCoordinates]);
|
|
292
336
|
const androidMapBaseUrl = useMemo(
|
|
293
337
|
() => resolveWebViewBaseUrlForDomain(env?.DOMAIN),
|
|
294
338
|
[],
|
|
295
339
|
);
|
|
340
|
+
const resolvedPopupTheme = useMemo(
|
|
341
|
+
() => resolveMapPopupTheme(popupTheme),
|
|
342
|
+
[popupTheme],
|
|
343
|
+
);
|
|
296
344
|
const androidMapHtml = useMemo(() => {
|
|
297
345
|
if (Platform.OS !== 'android') {
|
|
298
346
|
return '';
|
|
299
347
|
}
|
|
300
348
|
|
|
301
|
-
return buildAndroidWebMapHtml({
|
|
302
|
-
apiKey,
|
|
303
|
-
markerPayloads,
|
|
304
|
-
paths,
|
|
305
|
-
routeColor,
|
|
306
|
-
userCoordinates,
|
|
307
|
-
|
|
308
|
-
|
|
349
|
+
return buildAndroidWebMapHtml({
|
|
350
|
+
apiKey,
|
|
351
|
+
markerPayloads,
|
|
352
|
+
paths,
|
|
353
|
+
routeColor,
|
|
354
|
+
userCoordinates,
|
|
355
|
+
popupTheme: resolvedPopupTheme,
|
|
356
|
+
});
|
|
357
|
+
}, [apiKey, markerPayloads, paths, resolvedPopupTheme, routeColor, userCoordinates]);
|
|
309
358
|
|
|
310
359
|
useEffect(() => {
|
|
311
360
|
if (!selectedMarkerId) {
|
|
361
|
+
setSelectedRouteSummary(null);
|
|
312
362
|
return;
|
|
313
363
|
}
|
|
314
364
|
|
|
315
365
|
if (!selectedMarker) {
|
|
316
366
|
setSelectedMarkerId(null);
|
|
367
|
+
setSelectedRouteSummary(null);
|
|
317
368
|
}
|
|
318
369
|
}, [selectedMarker, selectedMarkerId]);
|
|
319
370
|
|
|
@@ -330,12 +381,13 @@ export default function DefaultNativeMap({
|
|
|
330
381
|
useEffect(() => {
|
|
331
382
|
if (!hasUserCoordinates || !selectedMarker || !apiKey || !Polyline) {
|
|
332
383
|
setRouteCoordinates([]);
|
|
384
|
+
setSelectedRouteSummary(null);
|
|
333
385
|
return undefined;
|
|
334
386
|
}
|
|
335
387
|
|
|
336
388
|
let cancelled = false;
|
|
337
389
|
|
|
338
|
-
|
|
390
|
+
fetchRouteData({
|
|
339
391
|
apiKey,
|
|
340
392
|
origin: {
|
|
341
393
|
latitude: Number(userCoordinates.latitude),
|
|
@@ -346,14 +398,21 @@ export default function DefaultNativeMap({
|
|
|
346
398
|
longitude: Number(selectedMarker.longitude),
|
|
347
399
|
},
|
|
348
400
|
})
|
|
349
|
-
.then(
|
|
401
|
+
.then(routeData => {
|
|
350
402
|
if (!cancelled) {
|
|
351
|
-
setRouteCoordinates(
|
|
403
|
+
setRouteCoordinates(
|
|
404
|
+
Array.isArray(routeData?.coordinates) ? routeData.coordinates : [],
|
|
405
|
+
);
|
|
406
|
+
setSelectedRouteSummary({
|
|
407
|
+
distanceLabel: String(routeData?.distanceLabel || '').trim(),
|
|
408
|
+
durationLabel: String(routeData?.durationLabel || '').trim(),
|
|
409
|
+
});
|
|
352
410
|
}
|
|
353
411
|
})
|
|
354
412
|
.catch(() => {
|
|
355
413
|
if (!cancelled) {
|
|
356
414
|
setRouteCoordinates([]);
|
|
415
|
+
setSelectedRouteSummary(null);
|
|
357
416
|
}
|
|
358
417
|
});
|
|
359
418
|
|
|
@@ -391,11 +450,11 @@ export default function DefaultNativeMap({
|
|
|
391
450
|
return () => clearTimeout(timeoutId);
|
|
392
451
|
}, [focusCoordinates, mapReady]);
|
|
393
452
|
|
|
394
|
-
const openExternalUrl = useCallback(url => {
|
|
395
|
-
const normalizedUrl = String(url || '').trim();
|
|
396
|
-
if (!normalizedUrl) {
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
453
|
+
const openExternalUrl = useCallback(url => {
|
|
454
|
+
const normalizedUrl = String(url || '').trim();
|
|
455
|
+
if (!normalizedUrl) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
399
458
|
|
|
400
459
|
Linking.openURL(normalizedUrl).catch(() => {});
|
|
401
460
|
}, []);
|
|
@@ -419,14 +478,14 @@ export default function DefaultNativeMap({
|
|
|
419
478
|
|
|
420
479
|
return true;
|
|
421
480
|
},
|
|
422
|
-
[openExternalUrl],
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
if (!apiKey || (markerPayloads.length === 0 && !hasUserCoordinates && pathCoordinates.length === 0)) {
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
if (Platform.OS === 'android') {
|
|
481
|
+
[openExternalUrl],
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
if (!apiKey || (markerPayloads.length === 0 && !hasUserCoordinates && pathCoordinates.length === 0)) {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (Platform.OS === 'android') {
|
|
430
489
|
return (
|
|
431
490
|
<View style={styles.mapContainer}>
|
|
432
491
|
<NativeWebView
|
|
@@ -530,86 +589,151 @@ export default function DefaultNativeMap({
|
|
|
530
589
|
onPress={() => {
|
|
531
590
|
setSelectedMarkerId(null);
|
|
532
591
|
setRouteCoordinates([]);
|
|
592
|
+
setSelectedRouteSummary(null);
|
|
533
593
|
}}
|
|
534
594
|
/>
|
|
535
595
|
) : null}
|
|
536
|
-
{Polyline && routeCoordinates.length > 1 ? (
|
|
537
|
-
<Polyline
|
|
538
|
-
coordinates={routeCoordinates}
|
|
539
|
-
strokeColor={routeColor}
|
|
540
|
-
strokeWidth={5}
|
|
541
|
-
/>
|
|
542
|
-
) : null}
|
|
543
|
-
{Polyline && Array.isArray(paths)
|
|
544
|
-
? paths.map(path => {
|
|
545
|
-
const from = extractMapCoordinates(path?.from || path?.origin || path?.start);
|
|
546
|
-
const to = extractMapCoordinates(path?.to || path?.destination || path?.end);
|
|
547
|
-
|
|
548
|
-
if (!from || !to) {
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return (
|
|
553
|
-
<Polyline
|
|
554
|
-
key={path?.id || `${from.latitude}:${from.longitude}-${to.latitude}:${to.longitude}`}
|
|
555
|
-
coordinates={[from, to]}
|
|
556
|
-
strokeColor={path?.color || routeColor}
|
|
557
|
-
strokeWidth={4}
|
|
558
|
-
/>
|
|
559
|
-
);
|
|
560
|
-
})
|
|
561
|
-
: null}
|
|
562
|
-
{markerPayloads.map(item => {
|
|
563
|
-
const navigationUrls = resolveMarkerNavigationUrls(item);
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
596
|
+
{Polyline && routeCoordinates.length > 1 ? (
|
|
597
|
+
<Polyline
|
|
598
|
+
coordinates={routeCoordinates}
|
|
599
|
+
strokeColor={routeColor}
|
|
600
|
+
strokeWidth={5}
|
|
601
|
+
/>
|
|
602
|
+
) : null}
|
|
603
|
+
{Polyline && Array.isArray(paths)
|
|
604
|
+
? paths.map(path => {
|
|
605
|
+
const from = extractMapCoordinates(path?.from || path?.origin || path?.start);
|
|
606
|
+
const to = extractMapCoordinates(path?.to || path?.destination || path?.end);
|
|
607
|
+
|
|
608
|
+
if (!from || !to) {
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return (
|
|
613
|
+
<Polyline
|
|
614
|
+
key={path?.id || `${from.latitude}:${from.longitude}-${to.latitude}:${to.longitude}`}
|
|
615
|
+
coordinates={[from, to]}
|
|
616
|
+
strokeColor={path?.color || routeColor}
|
|
617
|
+
strokeWidth={4}
|
|
618
|
+
/>
|
|
619
|
+
);
|
|
620
|
+
})
|
|
621
|
+
: null}
|
|
622
|
+
{markerPayloads.map(item => {
|
|
623
|
+
const navigationUrls = resolveMarkerNavigationUrls(item);
|
|
624
|
+
const title = item?.unitAlias || item?.alias || item?.companyName || item?.title;
|
|
625
|
+
const travelSummary =
|
|
626
|
+
selectedMarkerId === item.id
|
|
627
|
+
? buildTravelSummary(item, selectedRouteSummary)
|
|
628
|
+
: buildTravelSummary(item, null);
|
|
629
|
+
const logoFallback = String(title || 'CO')
|
|
630
|
+
.trim()
|
|
631
|
+
.split(/\s+/)
|
|
632
|
+
.slice(0, 2)
|
|
633
|
+
.map(chunk => chunk[0] || '')
|
|
634
|
+
.join('')
|
|
635
|
+
.toUpperCase();
|
|
636
|
+
|
|
637
|
+
return (
|
|
638
|
+
<Marker
|
|
639
|
+
key={item.id}
|
|
640
|
+
coordinate={{
|
|
641
|
+
latitude: Number(item.latitude),
|
|
642
|
+
longitude: Number(item.longitude),
|
|
643
|
+
}}
|
|
644
|
+
title={item.title}
|
|
645
|
+
description={item.addressLine}
|
|
646
|
+
onPress={() => setSelectedMarkerId(item.id)}>
|
|
647
|
+
{item.markerIconUrl ? (
|
|
648
|
+
<View style={styles.markerWrap}>
|
|
649
|
+
<Image
|
|
650
|
+
source={{uri: item.markerIconUrl}}
|
|
651
|
+
style={styles.markerIcon}
|
|
652
|
+
resizeMode="contain"
|
|
653
|
+
/>
|
|
654
|
+
</View>
|
|
655
|
+
) : null}
|
|
656
|
+
<Callout tooltip>
|
|
657
|
+
<View
|
|
658
|
+
style={[
|
|
659
|
+
styles.calloutCard,
|
|
660
|
+
{
|
|
661
|
+
backgroundColor: resolvedPopupTheme.modalBackground,
|
|
662
|
+
shadowColor: resolvedPopupTheme.modalShadow,
|
|
663
|
+
},
|
|
664
|
+
]}>
|
|
665
|
+
<View style={styles.headerRow}>
|
|
666
|
+
<View
|
|
667
|
+
style={[
|
|
668
|
+
styles.logoWrap,
|
|
669
|
+
{borderColor: resolvedPopupTheme.dividerBorder},
|
|
670
|
+
]}>
|
|
671
|
+
{item?.companyLogoUrl ? (
|
|
672
|
+
<Image
|
|
673
|
+
source={{uri: item.companyLogoUrl}}
|
|
674
|
+
style={styles.logoImage}
|
|
675
|
+
resizeMode="cover"
|
|
676
|
+
/>
|
|
677
|
+
) : (
|
|
678
|
+
<Text
|
|
679
|
+
style={[
|
|
680
|
+
styles.logoFallback,
|
|
681
|
+
{color: resolvedPopupTheme.buttonBackground},
|
|
682
|
+
]}>
|
|
683
|
+
{logoFallback}
|
|
684
|
+
</Text>
|
|
685
|
+
)}
|
|
686
|
+
</View>
|
|
687
|
+
<View style={styles.headerContent}>
|
|
688
|
+
<Text
|
|
689
|
+
style={[
|
|
690
|
+
styles.title,
|
|
691
|
+
{color: resolvedPopupTheme.modalHeaderText},
|
|
692
|
+
]}>
|
|
693
|
+
{title}
|
|
694
|
+
</Text>
|
|
695
|
+
</View>
|
|
696
|
+
</View>
|
|
697
|
+
{item.addressLine ? (
|
|
698
|
+
<Text style={[styles.line, {color: resolvedPopupTheme.modalText}]}>
|
|
699
|
+
{item.addressLine}
|
|
700
|
+
</Text>
|
|
701
|
+
) : null}
|
|
702
|
+
{item.addressExtra ? (
|
|
703
|
+
<Text style={[styles.line, {color: resolvedPopupTheme.modalText}]}>
|
|
704
|
+
{item.addressExtra}
|
|
705
|
+
</Text>
|
|
706
|
+
) : null}
|
|
707
|
+
|
|
708
|
+
{travelSummary ? (
|
|
709
|
+
<Text
|
|
710
|
+
style={[
|
|
711
|
+
styles.summaryText,
|
|
712
|
+
{color: resolvedPopupTheme.textMuted},
|
|
713
|
+
]}>
|
|
714
|
+
{travelSummary}
|
|
715
|
+
</Text>
|
|
716
|
+
) : null}
|
|
717
|
+
|
|
718
|
+
<View style={styles.actionsRow}>
|
|
719
|
+
<CalloutAction
|
|
720
|
+
label="Abrir no Maps"
|
|
721
|
+
onPress={() => openExternalUrl(navigationUrls.googleMapsUrl)}
|
|
722
|
+
popupTheme={resolvedPopupTheme}
|
|
723
|
+
primary
|
|
724
|
+
/>
|
|
725
|
+
<CalloutAction
|
|
726
|
+
label="Abrir no Waze"
|
|
727
|
+
onPress={() => openExternalUrl(navigationUrls.wazeUrl)}
|
|
728
|
+
popupTheme={resolvedPopupTheme}
|
|
729
|
+
primary
|
|
730
|
+
/>
|
|
731
|
+
</View>
|
|
732
|
+
</View>
|
|
733
|
+
</Callout>
|
|
734
|
+
</Marker>
|
|
735
|
+
);
|
|
736
|
+
})}
|
|
613
737
|
</NativeMapView>
|
|
614
738
|
);
|
|
615
739
|
}
|