@eohjsc/react-native-smart-city 0.7.41 → 0.7.43
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/package.json +1 -1
- package/src/commons/Dashboard/MyDashboardDevice/index.js +14 -12
- package/src/commons/Dashboard/MyUnit/index.js +4 -3
- package/src/commons/DateTimeRangeChange/index.js +5 -5
- package/src/commons/Device/HorizontalBarChart.js +11 -24
- package/src/commons/Device/PowerConsumptionChart.js +224 -100
- package/src/commons/Form/CurrencyInput.js +9 -18
- package/src/commons/SubUnit/Favorites/index.js +35 -10
- package/src/commons/SubUnit/ShortDetail.js +17 -4
- package/src/commons/SubUnit/__test__/Favorites.test.js +90 -0
- package/src/commons/SubUnit/__test__/ShortDetail.test.js +45 -2
- package/src/configs/AccessibilityLabel.js +1 -0
- package/src/hooks/IoT/__test__/useValueEvaluation.test.js +12 -12
- package/src/iot/UpdateStates.js +37 -16
- package/src/iot/mqtt.js +26 -1
- package/src/navigations/UnitStack.js +2 -2
- package/src/screens/ActivityLog/ItemLog.js +69 -16
- package/src/screens/ActivityLog/__test__/ItemLog.test.js +169 -2
- package/src/screens/ActivityLog/index.js +2 -2
- package/src/screens/ActivityLog/styles/itemLogStyles.js +35 -0
- package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +16 -5
- package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +10 -0
- package/src/screens/Device/__test__/DeviceDetail-modbus.test.js +37 -8
- package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +16 -5
- package/src/screens/Device/__test__/DeviceDetail.test.js +17 -6
- package/src/screens/Device/__test__/sensorDisplayItem.test.js +47 -11
- package/src/screens/Device/components/SensorDisplayItem.js +24 -5
- package/src/screens/Device/styles.js +4 -0
- package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +8 -49
- package/src/screens/UnitSummary/components/PowerConsumption/__test__/PowerConsumption.test.js +153 -6
- package/src/screens/UnitSummary/components/PowerConsumption/index.js +6 -47
- package/src/utils/I18n/translations/en.js +2 -0
- package/src/utils/I18n/translations/vi.js +2 -0
- package/src/utils/chartHelper/index.js +53 -0
- package/src/utils/chartHelper/getMaxValueIndex.js +0 -11
package/package.json
CHANGED
|
@@ -43,19 +43,21 @@ const MyDashboardDevice = ({ refreshing }) => {
|
|
|
43
43
|
);
|
|
44
44
|
const [myDashboardDevices, setMyDashboardDevices] = useState([]);
|
|
45
45
|
|
|
46
|
-
const
|
|
47
|
-
return
|
|
48
|
-
|
|
46
|
+
const chipIdsDashboardSet = useMemo(() => {
|
|
47
|
+
return new Set(
|
|
48
|
+
myDashboardDevices.flatMap((dashboard) =>
|
|
49
|
+
dashboard.devices.map((device) => device.chip_id)
|
|
50
|
+
)
|
|
49
51
|
);
|
|
50
52
|
}, [myDashboardDevices]);
|
|
51
53
|
|
|
52
54
|
const { chips, isFetching } = useChipJsonConfiguration({
|
|
53
|
-
ready: !!
|
|
55
|
+
ready: !!chipIdsDashboardSet.size,
|
|
54
56
|
});
|
|
55
57
|
|
|
56
58
|
const listChipsMqtt = useMemo(() => {
|
|
57
|
-
return chips.filter((chip) =>
|
|
58
|
-
}, [chips,
|
|
59
|
+
return chips.filter((chip) => chipIdsDashboardSet.has(chip.id));
|
|
60
|
+
}, [chips, chipIdsDashboardSet]);
|
|
59
61
|
|
|
60
62
|
const { mqttConfigs } = useConnectChipMqtt(listChipsMqtt);
|
|
61
63
|
|
|
@@ -81,14 +83,14 @@ const MyDashboardDevice = ({ refreshing }) => {
|
|
|
81
83
|
|
|
82
84
|
useWatchConfigs(isFetching ? [] : configsNeedWatching);
|
|
83
85
|
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
return
|
|
87
|
-
}, [listChipsMqtt,
|
|
86
|
+
const listSharedChipIds = useMemo(() => {
|
|
87
|
+
const chipMqttIdsSet = new Set(listChipsMqtt.map((chip) => chip.id));
|
|
88
|
+
return [...chipIdsDashboardSet].filter((id) => !chipMqttIdsSet.has(id));
|
|
89
|
+
}, [listChipsMqtt, chipIdsDashboardSet]);
|
|
88
90
|
|
|
89
91
|
useWatchSharedChips({
|
|
90
|
-
filterChipIds:
|
|
91
|
-
ready: !!
|
|
92
|
+
filterChipIds: listSharedChipIds,
|
|
93
|
+
ready: !!listSharedChipIds.length,
|
|
92
94
|
});
|
|
93
95
|
|
|
94
96
|
const fetchMyDashboardDevices = useCallback(async () => {
|
|
@@ -157,9 +157,10 @@ const MyUnit = ({ refreshing }) => {
|
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
const listChipsMqtt = useMemo(() => {
|
|
160
|
-
const
|
|
161
|
-
myUnitDetail?.abstract_devices.map((device) => device.chip_id) ?? []
|
|
162
|
-
|
|
160
|
+
const chipIdsUnitSet = new Set(
|
|
161
|
+
myUnitDetail?.abstract_devices.map((device) => device.chip_id) ?? []
|
|
162
|
+
);
|
|
163
|
+
return chips.filter((chip) => chipIdsUnitSet.has(chip.id));
|
|
163
164
|
}, [chips, myUnitDetail]);
|
|
164
165
|
|
|
165
166
|
useConnectChipMqtt(listChipsMqtt);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import React, { memo, useCallback, useEffect, useState } from 'react';
|
|
2
|
-
import { StyleSheet, View } from 'react-native';
|
|
3
1
|
import moment from 'moment/moment';
|
|
2
|
+
import { memo, useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { StyleSheet, View } from 'react-native';
|
|
4
4
|
import DateTimePickerModal from 'react-native-modal-datetime-picker';
|
|
5
5
|
|
|
6
|
-
import Text from '../Text';
|
|
7
6
|
import { Colors } from '../../configs';
|
|
8
|
-
import DateTimeButton from './DateTimeButton';
|
|
9
|
-
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
10
7
|
import { DATE_TIME_FORMAT } from '../../configs/Constants';
|
|
8
|
+
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
9
|
+
import Text from '../Text';
|
|
10
|
+
import DateTimeButton from './DateTimeButton';
|
|
11
11
|
|
|
12
12
|
const DateTimeRangeChange = memo(
|
|
13
13
|
({
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { isEmpty } from 'lodash';
|
|
2
|
-
import
|
|
2
|
+
import { memo, useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { StyleSheet, View } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import { Colors } from '../../configs';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
createDataLabelFormatter,
|
|
8
|
+
getMaxValueIndex,
|
|
9
|
+
} from '../../utils/chartHelper';
|
|
7
10
|
import Highcharts from '../Highcharts';
|
|
8
11
|
|
|
9
12
|
// https://github.com/facebook/hermes/issues/114#issuecomment-887106990
|
|
@@ -41,7 +44,7 @@ import Highcharts from '../Highcharts';
|
|
|
41
44
|
// );
|
|
42
45
|
// }
|
|
43
46
|
|
|
44
|
-
const HorizontalBarChart = memo(({ datas, config }) => {
|
|
47
|
+
const HorizontalBarChart = memo(({ datas, config, color = Colors.Primary }) => {
|
|
45
48
|
const [chartOptions, setChartOptions] = useState({
|
|
46
49
|
chart: {
|
|
47
50
|
type: 'bar',
|
|
@@ -92,23 +95,7 @@ const HorizontalBarChart = memo(({ datas, config }) => {
|
|
|
92
95
|
dataLabels: {
|
|
93
96
|
enabled: true,
|
|
94
97
|
useHTML: true,
|
|
95
|
-
formatter:
|
|
96
|
-
var _JSON$parse = JSON.parse(this.series.name),
|
|
97
|
-
unit = _JSON$parse.unit,
|
|
98
|
-
price = _JSON$parse.price;
|
|
99
|
-
var textColor = this.color === '#00979D' ? this.y === 0 ? '#262626' : '#FFFFFF' : '#262626';
|
|
100
|
-
var valueStyle = "color:" + textColor + ";font-weight:normal;font-size:12px;";
|
|
101
|
-
var costStyle = valueStyle + 'font-weight:bold;';
|
|
102
|
-
var label = "<span style=\\"" + valueStyle + "\\">" + ("" + this.y + unit);
|
|
103
|
-
if (price === '' || price === null || isNaN(price)) {
|
|
104
|
-
return label + '</span>';
|
|
105
|
-
}
|
|
106
|
-
var formatMoney = function formatMoney(number) {
|
|
107
|
-
var formatNumber = number.toFixed();
|
|
108
|
-
return parseInt(formatNumber, 10).toFixed(0).toString().replace(/(\\d)(?=(\\d\\d\\d)+(?!\\d))/g, '$1.') + 'đ';
|
|
109
|
-
};
|
|
110
|
-
return label + ("/<span style=\\"" + costStyle + "\\">") + formatMoney(this.y * price) + '</span></span>';
|
|
111
|
-
}`,
|
|
98
|
+
formatter: createDataLabelFormatter(),
|
|
112
99
|
align: 'right',
|
|
113
100
|
inside: false,
|
|
114
101
|
},
|
|
@@ -139,14 +126,14 @@ const HorizontalBarChart = memo(({ datas, config }) => {
|
|
|
139
126
|
useEffect(() => {
|
|
140
127
|
const dataY = (datas[0]?.data || []).map((item, index) => {
|
|
141
128
|
return {
|
|
142
|
-
color: index % 2 === 0 ?
|
|
129
|
+
color: index % 2 === 0 ? color + '50' : color + '30',
|
|
143
130
|
y: item.y,
|
|
144
131
|
};
|
|
145
132
|
});
|
|
146
133
|
const dataX = (datas[0]?.data || []).map((item) => item.x);
|
|
147
134
|
const maxY = getMaxValueIndex(dataY);
|
|
148
135
|
if (!isEmpty(maxY.max)) {
|
|
149
|
-
dataY.splice(maxY._index, 1, { ...maxY.max, color:
|
|
136
|
+
dataY.splice(maxY._index, 1, { ...maxY.max, color: color });
|
|
150
137
|
}
|
|
151
138
|
|
|
152
139
|
const isAllZero = dataY.every((item) => item.y === 0);
|
|
@@ -166,12 +153,12 @@ const HorizontalBarChart = memo(({ datas, config }) => {
|
|
|
166
153
|
},
|
|
167
154
|
series: [
|
|
168
155
|
{
|
|
169
|
-
name: JSON.stringify(config),
|
|
156
|
+
name: JSON.stringify({ ...config, color }),
|
|
170
157
|
data: dataY,
|
|
171
158
|
},
|
|
172
159
|
],
|
|
173
160
|
}));
|
|
174
|
-
}, [datas, config]);
|
|
161
|
+
}, [datas, config, color]);
|
|
175
162
|
|
|
176
163
|
return (
|
|
177
164
|
<View style={[styles.container, { height: heightChart }]}>
|
|
@@ -1,159 +1,283 @@
|
|
|
1
|
-
import React, { memo, useCallback, useState, useMemo, useEffect } from 'react';
|
|
2
|
-
import { StyleSheet, View } from 'react-native';
|
|
3
1
|
import moment from 'moment';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { ActivityIndicator, StyleSheet, View } from 'react-native';
|
|
4
4
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
5
5
|
|
|
6
|
-
import { Colors } from '../../configs';
|
|
7
|
-
import
|
|
6
|
+
import { API, Colors } from '../../configs';
|
|
7
|
+
import AccessibilityLabel from '../../configs/AccessibilityLabel';
|
|
8
|
+
import { axiosGet } from '../../utils/Apis/axios';
|
|
9
|
+
import { formatMoney } from '../../utils/Utils';
|
|
8
10
|
import Button from '../Button';
|
|
9
|
-
import
|
|
11
|
+
import ChartAggregationOption from '../ChartAggregationOption';
|
|
10
12
|
import DateTimeRangeChange from '../DateTimeRangeChange';
|
|
13
|
+
import CurrencyInput from '../Form/CurrencyInput';
|
|
14
|
+
import Text from '../Text';
|
|
11
15
|
import HorizontalBarChart from './HorizontalBarChart';
|
|
12
|
-
import ChartAggregationOption from '../ChartAggregationOption';
|
|
13
|
-
import { formatMoney } from '../../utils/Utils';
|
|
14
|
-
import AccessibilityLabel from '../../configs/AccessibilityLabel';
|
|
15
16
|
|
|
16
|
-
const PowerConsumptionChart =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
const PowerConsumptionChart = ({
|
|
18
|
+
label,
|
|
19
|
+
powerConfigId,
|
|
20
|
+
defaultPeriod = 'date',
|
|
21
|
+
color = Colors.Primary,
|
|
22
|
+
isWidgetOrder = false,
|
|
23
|
+
style,
|
|
24
|
+
}) => {
|
|
25
|
+
const t = useTranslations();
|
|
26
|
+
const [price, setPrice] = useState(null);
|
|
27
|
+
const [groupBy, setGroupBy] = useState(defaultPeriod);
|
|
28
|
+
const [datas, setDatas] = useState([]);
|
|
29
|
+
const [chartConfig, setChartConfig] = useState({
|
|
30
|
+
unit: 'kWh',
|
|
31
|
+
price: '',
|
|
32
|
+
});
|
|
33
|
+
const [value, setValue] = useState([moment().subtract(6, 'days'), moment()]);
|
|
34
|
+
const [isFetching, setIsFetching] = useState(false);
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const fetchData = useCallback(
|
|
37
|
+
async (startDate, endDate) => {
|
|
38
|
+
setIsFetching(true);
|
|
39
|
+
let params = new URLSearchParams();
|
|
40
|
+
params.append('config', powerConfigId);
|
|
41
|
+
params.append('group_by', groupBy);
|
|
42
|
+
if (groupBy === 'date') {
|
|
43
|
+
params.append('date_from', moment(startDate).format('YYYY-MM-DD'));
|
|
44
|
+
params.append('date_to', moment(endDate).format('YYYY-MM-DD'));
|
|
45
|
+
}
|
|
46
|
+
const { success, data } = await axiosGet(
|
|
47
|
+
API.VALUE_CONSUME.DISPLAY_HISTORY(),
|
|
48
|
+
{
|
|
49
|
+
params,
|
|
50
|
+
},
|
|
51
|
+
true
|
|
52
|
+
);
|
|
53
|
+
if (success) {
|
|
54
|
+
setDatas(data);
|
|
40
55
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
setIsFetching(false);
|
|
57
|
+
},
|
|
58
|
+
[groupBy, powerConfigId]
|
|
59
|
+
);
|
|
45
60
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
fetchData(value[0], value[1]);
|
|
63
|
+
}, [fetchData, value]);
|
|
49
64
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
]);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
setGroupBy(defaultPeriod);
|
|
67
|
+
}, [defaultPeriod]);
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
const onCalculateCost = useCallback(() => {
|
|
70
|
+
setChartConfig((config) => ({
|
|
71
|
+
...config,
|
|
72
|
+
price,
|
|
73
|
+
}));
|
|
74
|
+
}, [setChartConfig, price]);
|
|
58
75
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
const totalPrice = useMemo(() => {
|
|
77
|
+
const { price: chartPrice } = chartConfig;
|
|
78
|
+
if (chartPrice === '' || isNaN(chartPrice)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const sum =
|
|
82
|
+
datas[0] && datas[0].data
|
|
83
|
+
? datas[0].data.reduce((a, b) => a + b.y, 0)
|
|
84
|
+
: 0;
|
|
85
|
+
const roundedSum = sum * chartPrice;
|
|
86
|
+
return roundedSum.toFixed();
|
|
87
|
+
}, [datas, chartConfig]);
|
|
65
88
|
|
|
89
|
+
const renderChart = useMemo(() => {
|
|
90
|
+
if (isFetching) {
|
|
91
|
+
return (
|
|
92
|
+
<View style={styles.loadingWrapper}>
|
|
93
|
+
<ActivityIndicator size="large" color={Colors.Primary} />
|
|
94
|
+
</View>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (!datas || datas.length === 0) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
66
100
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<Text size={20} semibold color={Colors.Gray9}>
|
|
71
|
-
{t('history')}
|
|
72
|
-
</Text>
|
|
101
|
+
<HorizontalBarChart datas={datas} config={chartConfig} color={color} />
|
|
102
|
+
);
|
|
103
|
+
}, [chartConfig, datas, color, isFetching]);
|
|
73
104
|
|
|
105
|
+
const selectStart = (date) => {
|
|
106
|
+
setValue((state) => [date, state[1]]);
|
|
107
|
+
};
|
|
108
|
+
const selectEnd = (date) => {
|
|
109
|
+
setValue((state) => [state[0], date]);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<View style={[styles.wrapper, style]}>
|
|
114
|
+
<View style={styles.historyView}>
|
|
115
|
+
<View style={styles.titleHistory}>
|
|
116
|
+
<Text
|
|
117
|
+
style={styles.label}
|
|
118
|
+
bold
|
|
119
|
+
color={Colors.Gray9}
|
|
120
|
+
numberOfLines={1}
|
|
121
|
+
>
|
|
122
|
+
{label || t('power_consumption_chart')}
|
|
123
|
+
</Text>
|
|
124
|
+
|
|
125
|
+
{!isWidgetOrder && (
|
|
74
126
|
<ChartAggregationOption groupBy={groupBy} setGroupBy={setGroupBy} />
|
|
75
|
-
</View>
|
|
76
|
-
{groupBy === 'date' && (
|
|
77
|
-
<DateTimeRangeChange
|
|
78
|
-
startTime={value[0]}
|
|
79
|
-
endTime={value[1]}
|
|
80
|
-
selectStart={selectStart}
|
|
81
|
-
selectEnd={selectEnd}
|
|
82
|
-
/>
|
|
83
127
|
)}
|
|
84
128
|
</View>
|
|
129
|
+
{!isWidgetOrder && groupBy === 'date' && (
|
|
130
|
+
<DateTimeRangeChange
|
|
131
|
+
startTime={value[0]}
|
|
132
|
+
endTime={value[1]}
|
|
133
|
+
selectStart={selectStart}
|
|
134
|
+
selectEnd={selectEnd}
|
|
135
|
+
style={styles.dateTimeRange}
|
|
136
|
+
/>
|
|
137
|
+
)}
|
|
138
|
+
</View>
|
|
85
139
|
|
|
86
|
-
|
|
87
|
-
|
|
140
|
+
{!isWidgetOrder && (
|
|
141
|
+
<View style={styles.calculateCostCard}>
|
|
142
|
+
<Text style={styles.title}>
|
|
88
143
|
{t('input_price_to_calculate_electricity_cost')}
|
|
89
144
|
</Text>
|
|
90
|
-
<View style={styles.
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
145
|
+
<View style={styles.inputRow}>
|
|
146
|
+
<View style={styles.inputWrapper}>
|
|
147
|
+
<CurrencyInput
|
|
148
|
+
value={price}
|
|
149
|
+
onChange={setPrice}
|
|
150
|
+
minValue={0}
|
|
151
|
+
maxValue={100000000}
|
|
152
|
+
placeholder={'0'}
|
|
153
|
+
onSubmitEditing={onCalculateCost}
|
|
154
|
+
currency="đ/kWh"
|
|
155
|
+
wrapperStyle={styles.currencyInputNoBorder}
|
|
156
|
+
/>
|
|
157
|
+
</View>
|
|
99
158
|
<Button
|
|
100
159
|
type="primary"
|
|
101
160
|
title={t('calculate_cost')}
|
|
161
|
+
height={42}
|
|
102
162
|
onPress={onCalculateCost}
|
|
103
163
|
style={styles.buttonCalculate}
|
|
104
|
-
textSemiBold={
|
|
164
|
+
textSemiBold={true}
|
|
105
165
|
accessibilityLabel={AccessibilityLabel.BUTTON_CALCULATE_COST}
|
|
106
166
|
/>
|
|
107
167
|
</View>
|
|
108
168
|
</View>
|
|
169
|
+
)}
|
|
109
170
|
|
|
171
|
+
<View style={styles.chartCard}>
|
|
110
172
|
{renderChart}
|
|
111
173
|
{!!chartConfig.price && (
|
|
112
|
-
<
|
|
113
|
-
{t('total_power_price')}
|
|
114
|
-
<Text
|
|
174
|
+
<View style={styles.totalPriceRow}>
|
|
175
|
+
<Text style={styles.totalLabel}>{t('total_power_price')}</Text>
|
|
176
|
+
<Text
|
|
177
|
+
style={styles.totalValue}
|
|
178
|
+
accessibilityLabel={AccessibilityLabel.TOTAL_PRICE}
|
|
179
|
+
>
|
|
115
180
|
{formatMoney(totalPrice)}
|
|
116
181
|
</Text>
|
|
117
|
-
</
|
|
182
|
+
</View>
|
|
118
183
|
)}
|
|
119
184
|
</View>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
185
|
+
</View>
|
|
186
|
+
);
|
|
187
|
+
};
|
|
123
188
|
|
|
124
|
-
export default PowerConsumptionChart;
|
|
189
|
+
export default React.memo(PowerConsumptionChart);
|
|
125
190
|
|
|
126
191
|
const styles = StyleSheet.create({
|
|
192
|
+
wrapper: {
|
|
193
|
+
flex: 1,
|
|
194
|
+
marginBottom: 8,
|
|
195
|
+
},
|
|
127
196
|
historyView: {
|
|
128
|
-
paddingTop:
|
|
197
|
+
paddingTop: 0,
|
|
129
198
|
},
|
|
130
199
|
titleHistory: {
|
|
131
200
|
flexDirection: 'row',
|
|
132
201
|
alignItems: 'center',
|
|
133
202
|
justifyContent: 'space-between',
|
|
134
203
|
},
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
204
|
+
dateTimeRange: {
|
|
205
|
+
justifyContent: 'flex-end',
|
|
206
|
+
marginTop: 12,
|
|
207
|
+
marginLeft: 6,
|
|
208
|
+
flex: 1,
|
|
139
209
|
},
|
|
140
|
-
|
|
141
|
-
|
|
210
|
+
calculateCostCard: {
|
|
211
|
+
backgroundColor: Colors.LightPrimary,
|
|
212
|
+
borderRadius: 8,
|
|
213
|
+
padding: 16,
|
|
214
|
+
marginTop: 16,
|
|
142
215
|
},
|
|
143
|
-
|
|
144
|
-
|
|
216
|
+
title: {
|
|
217
|
+
fontSize: 14,
|
|
218
|
+
fontWeight: 'normal',
|
|
219
|
+
color: Colors.Primary,
|
|
220
|
+
marginBottom: 12,
|
|
145
221
|
},
|
|
146
|
-
|
|
147
|
-
marginTop: 12,
|
|
148
|
-
},
|
|
149
|
-
row: {
|
|
222
|
+
inputRow: {
|
|
150
223
|
flexDirection: 'row',
|
|
151
224
|
alignItems: 'center',
|
|
152
|
-
|
|
225
|
+
gap: 12,
|
|
226
|
+
},
|
|
227
|
+
inputWrapper: {
|
|
228
|
+
flex: 1,
|
|
229
|
+
backgroundColor: Colors.White,
|
|
230
|
+
borderWidth: 1,
|
|
231
|
+
borderColor: Colors.Cyan2,
|
|
232
|
+
borderRadius: 8,
|
|
233
|
+
height: 42,
|
|
234
|
+
justifyContent: 'center',
|
|
235
|
+
paddingHorizontal: 12,
|
|
236
|
+
},
|
|
237
|
+
currencyInputNoBorder: {
|
|
238
|
+
borderBottomWidth: 0,
|
|
153
239
|
},
|
|
154
240
|
buttonCalculate: {
|
|
155
241
|
width: null,
|
|
156
242
|
flex: null,
|
|
157
|
-
paddingHorizontal:
|
|
243
|
+
paddingHorizontal: 16,
|
|
244
|
+
borderRadius: 8,
|
|
245
|
+
},
|
|
246
|
+
chartCard: {
|
|
247
|
+
backgroundColor: Colors.White,
|
|
248
|
+
borderRadius: 12,
|
|
249
|
+
padding: 12,
|
|
250
|
+
marginTop: 16,
|
|
251
|
+
borderWidth: 1,
|
|
252
|
+
borderColor: Colors.Gray4,
|
|
253
|
+
},
|
|
254
|
+
totalPriceRow: {
|
|
255
|
+
flexDirection: 'row',
|
|
256
|
+
justifyContent: 'space-between',
|
|
257
|
+
alignItems: 'center',
|
|
258
|
+
paddingTop: 16,
|
|
259
|
+
marginTop: 12,
|
|
260
|
+
borderTopWidth: 1,
|
|
261
|
+
borderTopColor: Colors.Gray4,
|
|
262
|
+
},
|
|
263
|
+
totalLabel: {
|
|
264
|
+
fontSize: 14,
|
|
265
|
+
color: Colors.Gray8,
|
|
266
|
+
},
|
|
267
|
+
totalValue: {
|
|
268
|
+
fontSize: 18,
|
|
269
|
+
fontWeight: '700',
|
|
270
|
+
color: Colors.Primary,
|
|
271
|
+
},
|
|
272
|
+
label: {
|
|
273
|
+
fontSize: 18,
|
|
274
|
+
fontWeight: '700',
|
|
275
|
+
flex: 1,
|
|
276
|
+
marginRight: 8,
|
|
277
|
+
},
|
|
278
|
+
loadingWrapper: {
|
|
279
|
+
height: 150,
|
|
280
|
+
justifyContent: 'center',
|
|
281
|
+
alignItems: 'center',
|
|
158
282
|
},
|
|
159
283
|
});
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import {
|
|
3
|
-
View,
|
|
4
|
-
TextInput,
|
|
5
3
|
StyleSheet,
|
|
4
|
+
TextInput,
|
|
6
5
|
TouchableWithoutFeedback,
|
|
7
|
-
|
|
6
|
+
View,
|
|
8
7
|
} from 'react-native';
|
|
9
|
-
import Text from '../Text';
|
|
10
8
|
import { Colors } from '../../configs';
|
|
11
9
|
import AccessibilityLabel from '../../configs/AccessibilityLabel';
|
|
10
|
+
import Text from '../Text';
|
|
12
11
|
|
|
13
12
|
const formatNumber = (input, options) => {
|
|
14
13
|
const {
|
|
@@ -44,12 +43,13 @@ const formatNumber = (input, options) => {
|
|
|
44
43
|
const CurrencyInput = ({
|
|
45
44
|
value,
|
|
46
45
|
onChange,
|
|
47
|
-
currency
|
|
46
|
+
currency,
|
|
48
47
|
minValue,
|
|
49
48
|
maxValue,
|
|
50
49
|
precision = 0,
|
|
51
50
|
placeholder,
|
|
52
51
|
onSubmitEditing,
|
|
52
|
+
wrapperStyle,
|
|
53
53
|
}) => {
|
|
54
54
|
const [startingWithSign, setStartingWithSign] = useState();
|
|
55
55
|
const noNegativeValues = typeof minValue === 'number' && minValue >= 0;
|
|
@@ -119,16 +119,9 @@ const CurrencyInput = ({
|
|
|
119
119
|
: formattedValue;
|
|
120
120
|
}, [formattedValue, startingWithSign]);
|
|
121
121
|
|
|
122
|
-
const currencyMarginLeft = useMemo(() => {
|
|
123
|
-
if (Platform.OS === 'ios') {
|
|
124
|
-
return 5;
|
|
125
|
-
}
|
|
126
|
-
return textInputValue.toString().length < 2 ? -12 : 0;
|
|
127
|
-
}, [textInputValue]);
|
|
128
|
-
|
|
129
122
|
return (
|
|
130
123
|
<TouchableWithoutFeedback onPress={focusInput}>
|
|
131
|
-
<View style={styles.wrap}>
|
|
124
|
+
<View style={[styles.wrap, wrapperStyle]}>
|
|
132
125
|
<TextInput
|
|
133
126
|
ref={inputRef}
|
|
134
127
|
value={textInputValue}
|
|
@@ -140,10 +133,7 @@ const CurrencyInput = ({
|
|
|
140
133
|
maxLength={10}
|
|
141
134
|
accessibilityLabel={AccessibilityLabel.INPUT_CALCULATE_COST}
|
|
142
135
|
/>
|
|
143
|
-
<Text
|
|
144
|
-
semibold
|
|
145
|
-
style={[{ marginLeft: currencyMarginLeft }, styles.currency]}
|
|
146
|
-
>
|
|
136
|
+
<Text semibold style={styles.currency}>
|
|
147
137
|
{currency}
|
|
148
138
|
</Text>
|
|
149
139
|
</View>
|
|
@@ -164,6 +154,7 @@ const styles = StyleSheet.create({
|
|
|
164
154
|
input: {
|
|
165
155
|
fontSize: 16,
|
|
166
156
|
lineHeight: 24,
|
|
157
|
+
flex: 1,
|
|
167
158
|
},
|
|
168
159
|
currency: {
|
|
169
160
|
fontSize: 16,
|