@eohjsc/react-native-smart-city 0.7.41 → 0.7.42
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 +195 -103
- 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/hooks/IoT/__test__/useValueEvaluation.test.js +12 -12
- package/src/iot/UpdateStates.js +37 -16
- package/src/iot/mqtt.js +23 -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 +23 -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 +121 -6
- package/src/screens/UnitSummary/components/PowerConsumption/index.js +6 -47
- package/src/utils/I18n/translations/en.js +1 -0
- package/src/utils/I18n/translations/vi.js +1 -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,93 +1,130 @@
|
|
|
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 { 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
|
-
const PowerConsumptionChart = memo(
|
|
17
|
-
({
|
|
18
|
-
datas,
|
|
19
|
-
chartConfig,
|
|
20
|
-
setChartConfig,
|
|
21
|
-
style,
|
|
22
|
-
groupBy,
|
|
23
|
-
setGroupBy,
|
|
24
|
-
onChangeDate,
|
|
25
|
-
}) => {
|
|
26
|
-
const t = useTranslations();
|
|
27
|
-
const [price, setPrice] = useState(null);
|
|
28
16
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
const PowerConsumptionChart = ({
|
|
18
|
+
label,
|
|
19
|
+
powerConfigId,
|
|
20
|
+
defaultPeriod = 'date',
|
|
21
|
+
color = Colors.Primary,
|
|
22
|
+
style,
|
|
23
|
+
}) => {
|
|
24
|
+
const t = useTranslations();
|
|
25
|
+
const [price, setPrice] = useState(null);
|
|
26
|
+
const [groupBy, setGroupBy] = useState(defaultPeriod);
|
|
27
|
+
const [datas, setDatas] = useState([]);
|
|
28
|
+
const [chartConfig, setChartConfig] = useState({
|
|
29
|
+
unit: 'kWh',
|
|
30
|
+
price: '',
|
|
31
|
+
});
|
|
32
|
+
const [value, setValue] = useState([moment().subtract(6, 'days'), moment()]);
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
const fetchData = useCallback(
|
|
35
|
+
async (startDate, endDate) => {
|
|
36
|
+
let params = new URLSearchParams();
|
|
37
|
+
params.append('config', powerConfigId);
|
|
38
|
+
params.append('group_by', groupBy);
|
|
39
|
+
if (groupBy === 'date') {
|
|
40
|
+
params.append('date_from', moment(startDate).format('YYYY-MM-DD'));
|
|
41
|
+
params.append('date_to', moment(endDate).format('YYYY-MM-DD'));
|
|
40
42
|
}
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const { success, data } = await axiosGet(
|
|
44
|
+
API.VALUE_CONSUME.DISPLAY_HISTORY(),
|
|
45
|
+
{
|
|
46
|
+
params,
|
|
47
|
+
},
|
|
48
|
+
true
|
|
49
|
+
);
|
|
50
|
+
if (success) {
|
|
51
|
+
setDatas(data);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[groupBy, powerConfigId]
|
|
55
|
+
);
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
fetchData(value[0], value[1]);
|
|
59
|
+
}, [fetchData, value]);
|
|
49
60
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
]);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
setGroupBy(defaultPeriod);
|
|
63
|
+
}, [defaultPeriod]);
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
const onCalculateCost = useCallback(() => {
|
|
66
|
+
setChartConfig((config) => ({
|
|
67
|
+
...config,
|
|
68
|
+
price,
|
|
69
|
+
}));
|
|
70
|
+
}, [setChartConfig, price]);
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
const totalPrice = useMemo(() => {
|
|
73
|
+
const { price: chartPrice } = chartConfig;
|
|
74
|
+
if (chartPrice === '' || isNaN(chartPrice)) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const sum =
|
|
78
|
+
datas[0] && datas[0].data
|
|
79
|
+
? datas[0].data.reduce((a, b) => a + b.y, 0)
|
|
80
|
+
: 0;
|
|
81
|
+
const roundedSum = sum * chartPrice;
|
|
82
|
+
return roundedSum.toFixed();
|
|
83
|
+
}, [datas, chartConfig]);
|
|
65
84
|
|
|
85
|
+
const renderChart = useMemo(() => {
|
|
86
|
+
if (!datas || datas.length === 0) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
66
89
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<Text size={20} semibold color={Colors.Gray9}>
|
|
71
|
-
{t('history')}
|
|
72
|
-
</Text>
|
|
90
|
+
<HorizontalBarChart datas={datas} config={chartConfig} color={color} />
|
|
91
|
+
);
|
|
92
|
+
}, [chartConfig, datas, color]);
|
|
73
93
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
selectStart={selectStart}
|
|
81
|
-
selectEnd={selectEnd}
|
|
82
|
-
/>
|
|
83
|
-
)}
|
|
84
|
-
</View>
|
|
94
|
+
const selectStart = (date) => {
|
|
95
|
+
setValue((state) => [date, state[1]]);
|
|
96
|
+
};
|
|
97
|
+
const selectEnd = (date) => {
|
|
98
|
+
setValue((state) => [state[0], date]);
|
|
99
|
+
};
|
|
85
100
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
return (
|
|
102
|
+
<View style={style}>
|
|
103
|
+
<View style={styles.historyView}>
|
|
104
|
+
<View style={styles.titleHistory}>
|
|
105
|
+
<Text style={styles.label} bold color={Colors.Gray9}>
|
|
106
|
+
{label || t('power_consumption_chart')}
|
|
89
107
|
</Text>
|
|
90
|
-
|
|
108
|
+
|
|
109
|
+
<ChartAggregationOption groupBy={groupBy} setGroupBy={setGroupBy} />
|
|
110
|
+
</View>
|
|
111
|
+
{groupBy === 'date' && (
|
|
112
|
+
<DateTimeRangeChange
|
|
113
|
+
startTime={value[0]}
|
|
114
|
+
endTime={value[1]}
|
|
115
|
+
selectStart={selectStart}
|
|
116
|
+
selectEnd={selectEnd}
|
|
117
|
+
style={styles.dateTimeRange}
|
|
118
|
+
/>
|
|
119
|
+
)}
|
|
120
|
+
</View>
|
|
121
|
+
|
|
122
|
+
<View style={styles.calculateCostCard}>
|
|
123
|
+
<Text style={styles.title}>
|
|
124
|
+
{t('input_price_to_calculate_electricity_cost')}
|
|
125
|
+
</Text>
|
|
126
|
+
<View style={styles.inputRow}>
|
|
127
|
+
<View style={styles.inputWrapper}>
|
|
91
128
|
<CurrencyInput
|
|
92
129
|
value={price}
|
|
93
130
|
onChange={setPrice}
|
|
@@ -95,65 +132,120 @@ const PowerConsumptionChart = memo(
|
|
|
95
132
|
maxValue={100000000}
|
|
96
133
|
placeholder={'0'}
|
|
97
134
|
onSubmitEditing={onCalculateCost}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
type="primary"
|
|
101
|
-
title={t('calculate_cost')}
|
|
102
|
-
onPress={onCalculateCost}
|
|
103
|
-
style={styles.buttonCalculate}
|
|
104
|
-
textSemiBold={false}
|
|
105
|
-
accessibilityLabel={AccessibilityLabel.BUTTON_CALCULATE_COST}
|
|
135
|
+
currency="đ/kWh"
|
|
136
|
+
wrapperStyle={styles.currencyInputNoBorder}
|
|
106
137
|
/>
|
|
107
138
|
</View>
|
|
139
|
+
<Button
|
|
140
|
+
type="primary"
|
|
141
|
+
title={t('calculate_cost')}
|
|
142
|
+
height={42}
|
|
143
|
+
onPress={onCalculateCost}
|
|
144
|
+
style={styles.buttonCalculate}
|
|
145
|
+
textSemiBold={true}
|
|
146
|
+
accessibilityLabel={AccessibilityLabel.BUTTON_CALCULATE_COST}
|
|
147
|
+
/>
|
|
108
148
|
</View>
|
|
149
|
+
</View>
|
|
109
150
|
|
|
151
|
+
<View style={styles.chartCard}>
|
|
110
152
|
{renderChart}
|
|
111
153
|
{!!chartConfig.price && (
|
|
112
|
-
<
|
|
113
|
-
{t('total_power_price')}
|
|
114
|
-
<Text
|
|
154
|
+
<View style={styles.totalPriceRow}>
|
|
155
|
+
<Text style={styles.totalLabel}>{t('total_power_price')}</Text>
|
|
156
|
+
<Text
|
|
157
|
+
style={styles.totalValue}
|
|
158
|
+
accessibilityLabel={AccessibilityLabel.TOTAL_PRICE}
|
|
159
|
+
>
|
|
115
160
|
{formatMoney(totalPrice)}
|
|
116
161
|
</Text>
|
|
117
|
-
</
|
|
162
|
+
</View>
|
|
118
163
|
)}
|
|
119
164
|
</View>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
165
|
+
</View>
|
|
166
|
+
);
|
|
167
|
+
};
|
|
123
168
|
|
|
124
|
-
export default PowerConsumptionChart;
|
|
169
|
+
export default React.memo(PowerConsumptionChart);
|
|
125
170
|
|
|
126
171
|
const styles = StyleSheet.create({
|
|
127
172
|
historyView: {
|
|
128
|
-
paddingTop:
|
|
173
|
+
paddingTop: 0,
|
|
129
174
|
},
|
|
130
175
|
titleHistory: {
|
|
131
176
|
flexDirection: 'row',
|
|
132
177
|
alignItems: 'center',
|
|
133
178
|
justifyContent: 'space-between',
|
|
134
179
|
},
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
},
|
|
140
|
-
chartContainer: {
|
|
141
|
-
marginLeft: -8,
|
|
180
|
+
dateTimeRange: {
|
|
181
|
+
justifyContent: 'flex-end',
|
|
182
|
+
marginTop: 12,
|
|
183
|
+
marginLeft: 6,
|
|
142
184
|
},
|
|
143
|
-
|
|
144
|
-
|
|
185
|
+
calculateCostCard: {
|
|
186
|
+
backgroundColor: Colors.LightPrimary,
|
|
187
|
+
borderRadius: 8,
|
|
188
|
+
padding: 16,
|
|
189
|
+
marginTop: 16,
|
|
145
190
|
},
|
|
146
|
-
|
|
147
|
-
|
|
191
|
+
title: {
|
|
192
|
+
fontSize: 14,
|
|
193
|
+
fontWeight: 'normal',
|
|
194
|
+
color: Colors.Primary,
|
|
195
|
+
marginBottom: 12,
|
|
148
196
|
},
|
|
149
|
-
|
|
197
|
+
inputRow: {
|
|
150
198
|
flexDirection: 'row',
|
|
151
199
|
alignItems: 'center',
|
|
152
|
-
|
|
200
|
+
gap: 12,
|
|
201
|
+
},
|
|
202
|
+
inputWrapper: {
|
|
203
|
+
flex: 1,
|
|
204
|
+
backgroundColor: Colors.White,
|
|
205
|
+
borderWidth: 1,
|
|
206
|
+
borderColor: Colors.Cyan2,
|
|
207
|
+
borderRadius: 8,
|
|
208
|
+
height: 42,
|
|
209
|
+
justifyContent: 'center',
|
|
210
|
+
paddingHorizontal: 12,
|
|
211
|
+
},
|
|
212
|
+
currencyInputNoBorder: {
|
|
213
|
+
borderBottomWidth: 0,
|
|
153
214
|
},
|
|
154
215
|
buttonCalculate: {
|
|
155
216
|
width: null,
|
|
156
217
|
flex: null,
|
|
157
|
-
paddingHorizontal:
|
|
218
|
+
paddingHorizontal: 16,
|
|
219
|
+
borderRadius: 8,
|
|
220
|
+
},
|
|
221
|
+
chartCard: {
|
|
222
|
+
backgroundColor: Colors.White,
|
|
223
|
+
borderRadius: 12,
|
|
224
|
+
padding: 12,
|
|
225
|
+
marginTop: 16,
|
|
226
|
+
borderWidth: 1,
|
|
227
|
+
borderColor: Colors.Gray4,
|
|
228
|
+
},
|
|
229
|
+
totalPriceRow: {
|
|
230
|
+
flexDirection: 'row',
|
|
231
|
+
justifyContent: 'space-between',
|
|
232
|
+
alignItems: 'center',
|
|
233
|
+
paddingTop: 16,
|
|
234
|
+
marginTop: 12,
|
|
235
|
+
borderTopWidth: 1,
|
|
236
|
+
borderTopColor: Colors.Gray4,
|
|
237
|
+
},
|
|
238
|
+
totalLabel: {
|
|
239
|
+
fontSize: 14,
|
|
240
|
+
color: Colors.Gray8,
|
|
241
|
+
},
|
|
242
|
+
totalValue: {
|
|
243
|
+
fontSize: 18,
|
|
244
|
+
fontWeight: '700',
|
|
245
|
+
color: Colors.Primary,
|
|
246
|
+
},
|
|
247
|
+
label: {
|
|
248
|
+
fontSize: 18,
|
|
249
|
+
fontWeight: '700',
|
|
158
250
|
},
|
|
159
251
|
});
|
|
@@ -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,
|
|
@@ -4,6 +4,10 @@ import { useNavigation } from '@react-navigation/native';
|
|
|
4
4
|
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
5
5
|
import { useDevicesStatus } from '../../../hooks/Common';
|
|
6
6
|
import { useWatchConfigs } from '../../../hooks/IoT';
|
|
7
|
+
import useChipJsonConfiguration, {
|
|
8
|
+
useConnectChipMqtt,
|
|
9
|
+
} from '../../../hooks/useMqtt';
|
|
10
|
+
import useWatchSharedChips from '../../../hooks/IoT/useWatchSharedChips';
|
|
7
11
|
|
|
8
12
|
import { Section } from '../../Section';
|
|
9
13
|
import ItemDevice from '../../Device/ItemDevice';
|
|
@@ -11,7 +15,6 @@ import ItemOneTap from '../OneTap/ItemOneTap';
|
|
|
11
15
|
import ItemAddNew from '../../Device/ItemAddNew';
|
|
12
16
|
import styles from './styles';
|
|
13
17
|
import Routes from '../../../utils/Route';
|
|
14
|
-
import { DEVICE_TYPE } from '../../../configs/Constants';
|
|
15
18
|
|
|
16
19
|
const SubUnitFavorites = ({
|
|
17
20
|
isOwner,
|
|
@@ -25,22 +28,44 @@ const SubUnitFavorites = ({
|
|
|
25
28
|
|
|
26
29
|
useDevicesStatus(unit, favoriteDevices);
|
|
27
30
|
|
|
31
|
+
const favoriteChipIdsSet = useMemo(() => {
|
|
32
|
+
return new Set(favoriteDevices?.map((device) => device.chip_id));
|
|
33
|
+
}, [favoriteDevices]);
|
|
34
|
+
|
|
35
|
+
const { chips, isFetching } = useChipJsonConfiguration({
|
|
36
|
+
dashboardId: unit?.id,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const listChipsMqtt = useMemo(() => {
|
|
40
|
+
return chips.filter((chip) => favoriteChipIdsSet.has(chip.id));
|
|
41
|
+
}, [chips, favoriteChipIdsSet]);
|
|
42
|
+
|
|
43
|
+
const { mqttConfigs } = useConnectChipMqtt(listChipsMqtt);
|
|
44
|
+
|
|
28
45
|
const configsNeedWatching = useMemo(() => {
|
|
29
46
|
const configIds = [];
|
|
47
|
+
|
|
30
48
|
favoriteDevices.forEach((device) => {
|
|
31
|
-
if (
|
|
32
|
-
device?.quick_action?.config_id &&
|
|
33
|
-
![DEVICE_TYPE.GOOGLE_HOME, DEVICE_TYPE.LG_THINQ].includes(
|
|
34
|
-
device?.device_type
|
|
35
|
-
)
|
|
36
|
-
) {
|
|
49
|
+
if (device?.quick_action?.config_id && device?.is_managed_by_backend) {
|
|
37
50
|
configIds.push(device.quick_action.config_id);
|
|
38
51
|
}
|
|
39
52
|
});
|
|
40
|
-
return configIds;
|
|
41
|
-
}, [favoriteDevices]);
|
|
42
53
|
|
|
43
|
-
|
|
54
|
+
return configIds.filter((id) => !mqttConfigs[id]);
|
|
55
|
+
}, [favoriteDevices, mqttConfigs]);
|
|
56
|
+
|
|
57
|
+
useWatchConfigs(isFetching ? [] : configsNeedWatching);
|
|
58
|
+
|
|
59
|
+
const listSharedChipIds = useMemo(() => {
|
|
60
|
+
const chipMqttIdsSet = new Set(listChipsMqtt.map((chip) => chip.id));
|
|
61
|
+
return [...favoriteChipIdsSet].filter((id) => !chipMqttIdsSet.has(id));
|
|
62
|
+
}, [listChipsMqtt, favoriteChipIdsSet]);
|
|
63
|
+
|
|
64
|
+
useWatchSharedChips({
|
|
65
|
+
dashboardId: unit?.id,
|
|
66
|
+
filterChipIds: listSharedChipIds,
|
|
67
|
+
ready: !!listSharedChipIds.length,
|
|
68
|
+
});
|
|
44
69
|
|
|
45
70
|
const handleOnAddNew = () => {
|
|
46
71
|
navigate(Routes.SelectAddToFavorites, {
|