@eohjsc/react-native-smart-city 0.2.95 → 0.2.96

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 (50) hide show
  1. package/package.json +2 -2
  2. package/src/Images/Common/member.svg +4 -0
  3. package/src/Images/Common/owner.svg +3 -0
  4. package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +5 -1
  5. package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +5 -1
  6. package/src/commons/ActionGroup/__test__/OptionsDropdownTemplate.test.js +2 -2
  7. package/src/commons/Connecting /__test__/Connecting.test.js +23 -0
  8. package/src/commons/Connecting /index.js +67 -0
  9. package/src/commons/Connecting /styles.js +28 -0
  10. package/src/commons/ConnectingProcess/index.js +3 -54
  11. package/src/commons/MenuActionMore/index.js +8 -1
  12. package/src/commons/Sharing/MemberList.js +5 -10
  13. package/src/commons/Sharing/RowMember.js +128 -38
  14. package/src/commons/Sharing/__test__/MemberList.test.js +3 -3
  15. package/src/commons/Sharing/__test__/RowMember.test.js +4 -4
  16. package/src/configs/API.js +10 -0
  17. package/src/configs/Constants.js +10 -0
  18. package/src/hooks/Common/useSensorsStatus.js +33 -23
  19. package/src/navigations/UnitStack.js +16 -0
  20. package/src/screens/AddNewGateway/PlugAndPlay/ConnectWifiWarning.js +12 -3
  21. package/src/screens/AddNewGateway/PlugAndPlay/FirstWarning.js +5 -1
  22. package/src/screens/AddNewGateway/PlugAndPlay/GatewayWifiList.js +9 -1
  23. package/src/screens/EditActionsList/Styles/indexStyles.js +1 -0
  24. package/src/screens/EmergencyContacts/EmergencyContactsList.js +1 -1
  25. package/src/screens/EmergencyContacts/EmergencyContactsSelectContacts.js +29 -11
  26. package/src/screens/EmergencyContacts/__test__/EmergencyContactList.test.js +1 -0
  27. package/src/screens/EmergencyContacts/__test__/EmergencyContactsSelectContacts.test.js +56 -0
  28. package/src/screens/EnterPassword/__test__/EnterPassword.test.js +124 -0
  29. package/src/screens/EnterPassword/index.js +84 -0
  30. package/src/screens/EnterPassword/styles.js +36 -0
  31. package/src/screens/Sharing/Components/ItemChangeRole.js +43 -0
  32. package/src/screens/Sharing/Components/SensorItem.js +7 -1
  33. package/src/screens/Sharing/Components/Styles/ItemChangeRoleStyles.js +35 -0
  34. package/src/screens/Sharing/Components/__test__/ItemChangeRole.test.js +37 -0
  35. package/src/screens/Sharing/Components/__test__/SensorItem.test.js +53 -0
  36. package/src/screens/Sharing/InfoMemberUnit.js +274 -0
  37. package/src/screens/Sharing/MemberList.js +50 -53
  38. package/src/screens/Sharing/SelectPermission.js +93 -12
  39. package/src/screens/Sharing/Styles/inforMemberUnitStyles.js +92 -0
  40. package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +121 -0
  41. package/src/screens/Sharing/__test__/MemberList.test.js +9 -24
  42. package/src/screens/Sharing/__test__/SelectPermission.test.js +53 -0
  43. package/src/screens/Sharing/hooks/index.js +76 -32
  44. package/src/screens/Unit/Detail.js +12 -10
  45. package/src/screens/Unit/ManageUnit.js +5 -5
  46. package/src/screens/Unit/ManageUnitStyles.js +1 -0
  47. package/src/utils/I18n/translations/en.json +13 -1
  48. package/src/utils/I18n/translations/vi.json +11 -1
  49. package/src/utils/Route/index.js +2 -0
  50. package/src/utils/Utils.js +21 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eohjsc/react-native-smart-city",
3
3
  "title": "React Native Smart Home",
4
- "version": "0.2.95",
4
+ "version": "0.2.96",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -97,7 +97,7 @@
97
97
  "dependencies": {
98
98
  "@ant-design/icons-react-native": "^2.2.1",
99
99
  "@ant-design/react-native": "^4.0.5",
100
- "@eohjsc/highcharts": "^1.0.6",
100
+ "@eohjsc/highcharts": "^1.0.8",
101
101
  "@eohjsc/react-native-keyboard-aware-scroll-view": "^0.9.5",
102
102
  "@formatjs/intl-getcanonicallocales": "^1.4.5",
103
103
  "@formatjs/intl-numberformat": "^5.6.2",
@@ -0,0 +1,4 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M12 6C13.1046 6 14 5.10457 14 4C14 2.89543 13.1046 2 12 2C10.8954 2 10 2.89543 10 4C10 5.10457 10.8954 6 12 6Z" fill="#262626"/>
3
+ <path d="M15.89 8.11C15.5 7.72 14.83 7 13.53 7C13.32 7 12.11 7 10.99 7C8.24 6.99 6 4.75 6 2H4C4 5.16 6.11 7.84 9 8.71V22H11V16H13V22H15V10.05L18.95 14L20.36 12.59L15.89 8.11Z" fill="#262626"/>
4
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M21.753 9.0184C21.753 9.66253 21.2308 10.1847 20.5867 10.1847C20.4682 10.1847 20.3559 10.1617 20.2483 10.1291L18.5961 15.808H5.15687L3.50472 10.1291C3.39711 10.1617 3.28483 10.1847 3.16633 10.1847C2.52221 10.1847 2 9.66253 2 9.0184C2 8.37428 2.52221 7.85207 3.16633 7.85207C3.81046 7.85207 4.33266 8.37428 4.33266 9.0184C4.33266 9.24732 4.26424 9.45912 4.15041 9.6392L7.88142 12.1939L8.36195 7.3196C7.78003 7.25367 7.32625 6.76567 7.32625 6.16633C7.32625 5.52221 7.84846 5 8.49258 5C9.13671 5 9.65892 5.52221 9.65892 6.16633C9.65892 6.59305 9.42752 6.96224 9.08539 7.16565L11.8765 11.7398L14.6863 7.13517C14.3743 6.92585 14.1687 6.57035 14.1687 6.16633C14.1687 5.52221 14.6909 5 15.3351 5C15.9792 5 16.5014 5.52221 16.5014 6.16633C16.5014 6.79118 16.0091 7.2969 15.392 7.32707L15.8716 12.1939L19.6026 9.6392C19.4888 9.45912 19.4203 9.24732 19.4203 9.0184C19.4203 8.37428 19.9425 7.85207 20.5867 7.85207C21.2308 7.85207 21.753 8.37428 21.753 9.0184ZM5.15782 16.6384H18.5962V18.712H5.15782V16.6384Z" fill="#262626"/>
3
+ </svg>
@@ -39,7 +39,11 @@ const NumberUpDownActionTemplate = ({ actionGroup, doAction, sensor }) => {
39
39
 
40
40
  const doActionAndWatchConfig = useCallback(
41
41
  (actionData, actionValue, actionName) => {
42
- doAction(actionData, actionValue, actionName);
42
+ doAction(
43
+ actionData,
44
+ JSON.stringify({ temperature: actionValue }),
45
+ actionName
46
+ );
43
47
  if (!sensor?.is_managed_by_backend) {
44
48
  return;
45
49
  }
@@ -61,7 +61,11 @@ const OptionsDropdownActionTemplate = ({ actionGroup, doAction, sensor }) => {
61
61
  const value = getOptionValue(newOption);
62
62
  let actionName = `${sensor?.name} ${title?.toLowerCase()} ${value}`;
63
63
  actionName = actionName.replace(/\s+/g, ' ').trim();
64
- doAction(action_data, value, actionName);
64
+ doAction(
65
+ action_data,
66
+ JSON.stringify({ level: value, key_code: newOption?.value_int }),
67
+ actionName
68
+ );
65
69
  if (sensor?.is_managed_by_backend) {
66
70
  configuration.config && watchMultiConfigs([configuration.config]);
67
71
  }
@@ -152,7 +152,7 @@ describe('Test OptionsDropdownActionTemplate', () => {
152
152
 
153
153
  expect(mockDoAction).toHaveBeenCalledWith(
154
154
  action_data,
155
- 1,
155
+ JSON.stringify({ level: 1, key_code: 1 }),
156
156
  'Sensor name fan speed 1'
157
157
  );
158
158
  is_managed_by_backend
@@ -221,7 +221,7 @@ describe('Test OptionsDropdownActionTemplate', () => {
221
221
 
222
222
  expect(mockDoAction).toHaveBeenCalledWith(
223
223
  action_data,
224
- 'level-1',
224
+ JSON.stringify({ level: 'level-1', key_code: 1 }),
225
225
  'Sensor name fan speed level-1'
226
226
  ); // doAction with text instead of int
227
227
 
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import renderer, { act } from 'react-test-renderer';
3
+ import Connecting from '../index';
4
+ import Text from '../../Text';
5
+ import { SCProvider } from '../../../context';
6
+ import { mockSCStore } from '../../../context/mockStore';
7
+
8
+ const wrapComponent = (route) => (
9
+ <SCProvider initState={mockSCStore({})}>
10
+ <Connecting route={route} />
11
+ </SCProvider>
12
+ );
13
+ describe('Test Connecting', () => {
14
+ let tree;
15
+ test('create Connecting', () => {
16
+ act(() => {
17
+ tree = renderer.create(wrapComponent());
18
+ });
19
+ const instance = tree.root;
20
+ const text = instance.findAllByType(Text);
21
+ expect(text).toHaveLength(4);
22
+ });
23
+ });
@@ -0,0 +1,67 @@
1
+ import React, { useEffect, useCallback, useState } from 'react';
2
+ import { View } from 'react-native';
3
+ import * as Progress from 'react-native-progress';
4
+ import Text from '../Text';
5
+ import { Colors, Constants } from '../../configs';
6
+ import styles from './styles';
7
+ import { useTranslations } from '../../hooks/Common/useTranslations';
8
+ import { HeaderCustom } from '../Header';
9
+
10
+ const Connecting = ({ isLoading, isConnect, isPercentConnect }) => {
11
+ const t = useTranslations();
12
+ const [percent, setPercent] = useState(0);
13
+
14
+ useEffect(() => {
15
+ processLoading();
16
+ }, [processLoading]);
17
+
18
+ const processLoading = useCallback(() => {
19
+ let interval;
20
+ if (isLoading) {
21
+ interval = setInterval(() => {
22
+ setPercent((prev) => {
23
+ if (prev === 1) {
24
+ clearInterval(interval);
25
+ return 1;
26
+ } else if (prev === 0.8 && isConnect) {
27
+ clearInterval(interval);
28
+ return 0.8;
29
+ } else {
30
+ return prev + 0.2;
31
+ }
32
+ });
33
+ }, 180);
34
+ } else {
35
+ clearInterval(interval);
36
+ }
37
+ }, [isConnect, isLoading]);
38
+ return (
39
+ <View style={styles.screen}>
40
+ <HeaderCustom title={t('connect_device')} isShowSeparator />
41
+ <View style={styles.body}>
42
+ <View style={styles.connecting}>
43
+ <Text type="H4" bold>
44
+ {t('connecting')}
45
+ </Text>
46
+ </View>
47
+ <View style={styles.percentLoad}>
48
+ <Progress.Bar
49
+ progress={isPercentConnect || percent}
50
+ animated={true}
51
+ color={Colors.Primary}
52
+ indeterminateAnimationDuration={1000}
53
+ height={7}
54
+ width={Constants.width - 80}
55
+ useNativeDriver={true}
56
+ />
57
+ <Text style={styles.textPercentLoad}>{`${parseInt(
58
+ (isPercentConnect || percent) * 100,
59
+ 10
60
+ )}%`}</Text>
61
+ </View>
62
+ </View>
63
+ </View>
64
+ );
65
+ };
66
+
67
+ export default Connecting;
@@ -0,0 +1,28 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { Constants } from '../../configs';
3
+
4
+ export default StyleSheet.create({
5
+ screen: {
6
+ flex: 1,
7
+ width: '100%',
8
+ },
9
+ body: {
10
+ width: '100%',
11
+ height: Constants.height - 50,
12
+ justifyContent: 'center',
13
+ alignItems: 'center',
14
+ },
15
+ connecting: {
16
+ flexDirection: 'row',
17
+ justifyContent: 'center',
18
+ marginBottom: 10,
19
+ },
20
+ percentLoad: {
21
+ flexDirection: 'row',
22
+ justifyContent: 'center',
23
+ alignItems: 'center',
24
+ },
25
+ textPercentLoad: {
26
+ marginLeft: 10,
27
+ },
28
+ });
@@ -3,14 +3,14 @@ import { SafeAreaView, View, TouchableOpacity } from 'react-native';
3
3
  import { useTranslations } from '../../hooks/Common/useTranslations';
4
4
  import Routes from '../../utils/Route';
5
5
  import { useNavigation } from '@react-navigation/native';
6
- import * as Progress from 'react-native-progress';
7
6
  import ImageSuccessfully from '../../Images/Common/SuccessfullyConnected.svg';
8
7
 
9
8
  import Text from '../Text';
10
9
  import { axiosPost } from '../../utils/Apis/axios';
11
- import { API, Colors, Constants } from '../../configs';
10
+ import { API, Colors } from '../../configs';
12
11
  import styles from './styles';
13
12
  import DeviceItem from './DeviceItem/DeviceItem';
13
+ import Connecting from '../Connecting ';
14
14
  import { useSCContextSelector } from '../../context';
15
15
 
16
16
  const ConnectingProcess = ({ route }) => {
@@ -29,31 +29,9 @@ const ConnectingProcess = ({ route }) => {
29
29
  wifi_pass,
30
30
  chip_id,
31
31
  } = route.params;
32
- const [percent, setPercent] = useState(0);
33
32
  const [isLoading, setIsLoading] = useState(true);
34
33
  const [sensor, setSensor] = useState(null);
35
34
  const user = useSCContextSelector((state) => state?.auth?.account?.user);
36
- useEffect(() => {
37
- processLoading();
38
- }, [processLoading]);
39
-
40
- const processLoading = useCallback(() => {
41
- let interval;
42
- if (isLoading) {
43
- interval = setInterval(() => {
44
- setPercent((prev) => {
45
- if (prev === 1) {
46
- clearInterval(interval);
47
- return 1;
48
- } else {
49
- return prev + 0.2;
50
- }
51
- });
52
- }, 180);
53
- } else {
54
- clearInterval(interval);
55
- }
56
- }, [isLoading]);
57
35
 
58
36
  const ConnectingDevice = useCallback(async () => {
59
37
  setIsLoading(true);
@@ -120,35 +98,6 @@ const ConnectingProcess = ({ route }) => {
120
98
  chip_id,
121
99
  ]);
122
100
 
123
- const Connecting = useCallback(() => {
124
- return (
125
- <>
126
- <View style={styles.progressBar}>
127
- <View style={styles.connecting}>
128
- <Text type="H4" bold>
129
- {t('connecting')}
130
- </Text>
131
- </View>
132
- </View>
133
- <View style={styles.percentLoad}>
134
- <Progress.Bar
135
- progress={percent}
136
- animated={true}
137
- color={Colors.Primary}
138
- indeterminateAnimationDuration={1000}
139
- height={7}
140
- width={Constants.width - 80}
141
- useNativeDriver={true}
142
- />
143
- <Text style={styles.textPercentLoad}>{`${parseInt(
144
- percent * 100,
145
- 10
146
- )}%`}</Text>
147
- </View>
148
- </>
149
- );
150
- }, [percent, t]);
151
-
152
101
  const ConnectingSuccess = useCallback(() => {
153
102
  return (
154
103
  <View style={styles.ConnectingSuccess}>
@@ -188,7 +137,7 @@ const ConnectingProcess = ({ route }) => {
188
137
  <Text bold style={styles.connectingText}>
189
138
  {t('connect_device')}
190
139
  </Text>
191
- {!!isLoading && <Connecting />}
140
+ {!!isLoading && <Connecting isLoading={isLoading} />}
192
141
  {!isLoading && <ConnectingSuccess />}
193
142
  </View>
194
143
  {!isLoading && (
@@ -1,4 +1,4 @@
1
- import React, { memo, useCallback, useEffect } from 'react';
1
+ import React, { memo, useCallback, useEffect, useState } from 'react';
2
2
  import { TouchableOpacity, ScrollView } from 'react-native';
3
3
  import Popover from 'react-native-popover-view';
4
4
 
@@ -19,10 +19,16 @@ const MenuActionMore = memo(
19
19
  wrapStyle,
20
20
  isTextCenter = true,
21
21
  }) => {
22
+ const [isDisable, setIsDisable] = useState(false);
22
23
  const onPress = useCallback(
23
24
  (item, index) => () => {
25
+ setIsDisable(true);
24
26
  hideMore && hideMore();
25
27
  onItemClick && onItemClick(item, index);
28
+ const timeout = setTimeout(() => {
29
+ setIsDisable(false);
30
+ clearTimeout(timeout);
31
+ }, 500);
26
32
  },
27
33
  [hideMore, onItemClick]
28
34
  );
@@ -55,6 +61,7 @@ const MenuActionMore = memo(
55
61
  onPress={onPress(item, index)}
56
62
  key={index}
57
63
  testID={TESTID.TOUCHABLE_ACTION_ADD_MORE}
64
+ disabled={isDisable}
58
65
  >
59
66
  <Text style={styles.modalHeaderText}>{item.text}</Text>
60
67
  </TouchableOpacity>
@@ -1,29 +1,27 @@
1
- import React from 'react';
1
+ import React, { memo } from 'react';
2
2
  import { View, StyleSheet } from 'react-native';
3
3
  import { useTranslations } from '../../hooks/Common/useTranslations';
4
4
  import { Colors } from '../../configs';
5
5
  import Text from '../../commons/Text';
6
-
7
6
  import RowMember from './RowMember';
8
7
 
9
8
  const MemberList = ({
10
9
  dataMember,
11
10
  ownerId,
11
+ unit,
12
12
  currentUserId, //user is using app
13
- onPressRemove,
14
13
  }) => {
15
14
  const t = useTranslations();
16
-
17
15
  return (
18
16
  <View style={styles.box}>
19
17
  {!!dataMember.length &&
20
18
  dataMember.map((item, index) => (
21
19
  <RowMember
22
20
  member={item}
21
+ unit={unit}
23
22
  index={index}
24
23
  ownerId={ownerId}
25
24
  currentUserId={currentUserId}
26
- onPressRemove={onPressRemove}
27
25
  key={index.toString()}
28
26
  />
29
27
  ))}
@@ -33,13 +31,10 @@ const MemberList = ({
33
31
  </View>
34
32
  );
35
33
  };
36
- export default MemberList;
34
+ export default memo(MemberList);
35
+
37
36
  const styles = StyleSheet.create({
38
37
  box: {
39
- paddingBottom: 16,
40
- borderRadius: 20,
41
38
  backgroundColor: Colors.White,
42
- borderWidth: 1,
43
- borderColor: Colors.Gray4,
44
39
  },
45
40
  });
@@ -1,13 +1,12 @@
1
1
  import React, { memo, useMemo } from 'react';
2
- import { StyleSheet, View } from 'react-native';
3
- import { IconOutline } from '@ant-design/icons-react-native';
2
+ import { StyleSheet, View, TouchableOpacity, Image } from 'react-native';
4
3
  import { useTranslations } from '../../hooks/Common/useTranslations';
5
-
6
4
  import { Colors } from '../../configs';
7
5
  import Text from '../../commons/Text';
8
6
  import { shortEmailName } from '../../utils/Utils';
9
-
10
- import BtnRemoveMember from './BtnRemoveMember';
7
+ import { CircleView } from '../CircleView';
8
+ import { useNavigation } from '@react-navigation/native';
9
+ import Routes from '../../utils/Route';
11
10
 
12
11
  const arrColor = [
13
12
  Colors.GeekBlue3,
@@ -19,46 +18,88 @@ const arrColor = [
19
18
  Colors.Cyan2,
20
19
  ];
21
20
  const RowMember = memo(
22
- ({ member, index, ownerId, currentUserId, onPressRemove }) => {
21
+ ({
22
+ member,
23
+ index,
24
+ ownerId,
25
+ unit,
26
+ currentUserId,
27
+ type,
28
+ leftIcon = true,
29
+ rightComponent = true,
30
+ }) => {
23
31
  const t = useTranslations();
24
- const canRemoveMember = useMemo(
25
- () => ownerId === currentUserId && member.id !== ownerId,
26
- [currentUserId, member.id, ownerId]
27
- );
32
+ const { navigate } = useNavigation();
28
33
  const [role, roleColor] = useMemo(
29
34
  () =>
30
- member.id === ownerId
35
+ member?.id === ownerId
31
36
  ? [t('owner'), Colors.Primary]
32
- : member.id === currentUserId
33
- ? [t('me'), Colors.Gray6]
34
- : ['', ''],
35
- [currentUserId, member.id, ownerId, t]
37
+ : member?.id === currentUserId
38
+ ? [t('me'), Colors.Primary]
39
+ : [t('member'), Colors.Gray6],
40
+ [currentUserId, member?.id, ownerId, t]
36
41
  );
37
- const paddingBottom = role ? 16 : 23;
42
+ const firstWordsInName = useMemo(() => {
43
+ const wordTemp = member?.name || shortEmailName(member?.email) || '';
44
+ return wordTemp?.charAt();
45
+ }, [member?.email, member?.name]);
46
+
38
47
  if (member?.id === ownerId && member?.share_id) {
39
48
  return null;
40
49
  }
50
+ const circleColorTypes = {
51
+ primary: 'primary',
52
+ disable: 'disable',
53
+ noneBG: 'none',
54
+ };
55
+ const circleColor = type
56
+ ? circleColorTypes[type]
57
+ : arrColor[index % arrColor.length];
58
+
59
+ const onPressInfo = () => {
60
+ navigate(Routes.UnitMemberInformation, {
61
+ member,
62
+ ownerId,
63
+ unit,
64
+ });
65
+ };
41
66
  return (
42
67
  <View style={styles.rowContainer}>
43
- <View
44
- style={[
45
- styles.iconContainer,
46
- { backgroundColor: arrColor[index % arrColor.length] },
47
- ]}
48
- >
49
- <IconOutline name={'user'} size={20} color={Colors.White} />
50
- </View>
51
- <View style={[styles.infoContainer, { paddingBottom: paddingBottom }]}>
52
- <Text style={styles.textName}>
53
- {member?.name || shortEmailName(member.email) || ''}
54
- </Text>
55
- {!!role && (
56
- <Text style={[styles.textRole, { color: roleColor }]}>{role}</Text>
57
- )}
58
- {canRemoveMember && (
59
- <BtnRemoveMember onPressRemove={onPressRemove} member={member} />
60
- )}
61
- </View>
68
+ <TouchableOpacity onPress={onPressInfo} disabled={type === 'disable'}>
69
+ <View style={styles.Border}>
70
+ {!!leftIcon && (
71
+ <View style={styles.paddingLeft16}>
72
+ {member?.avatar ? (
73
+ <Image
74
+ source={{ uri: member?.avatar }}
75
+ style={styles.avatar}
76
+ />
77
+ ) : (
78
+ <CircleView size={40} backgroundColor={circleColor} center>
79
+ <Text color={Colors.White}>{firstWordsInName}</Text>
80
+ </CircleView>
81
+ )}
82
+ </View>
83
+ )}
84
+ <View style={styles.columnFlex}>
85
+ <Text style={styles.titleName}>
86
+ {member?.name || shortEmailName(member?.email) || ''}
87
+ </Text>
88
+ {member?.phone_number && (
89
+ <Text style={styles.status}> {member?.phone_number}</Text>
90
+ )}
91
+ </View>
92
+ {!!rightComponent && (
93
+ <View style={styles.endFlex}>
94
+ {!!role && (
95
+ <Text style={[styles.textRole, { color: roleColor }]}>
96
+ {role}
97
+ </Text>
98
+ )}
99
+ </View>
100
+ )}
101
+ </View>
102
+ </TouchableOpacity>
62
103
  </View>
63
104
  );
64
105
  }
@@ -68,9 +109,7 @@ export default RowMember;
68
109
 
69
110
  const styles = StyleSheet.create({
70
111
  rowContainer: {
71
- flexDirection: 'row',
72
- alignItems: 'center',
73
- paddingHorizontal: 16,
112
+ flex: 1,
74
113
  },
75
114
  iconContainer: {
76
115
  alignItems: 'center',
@@ -107,4 +146,55 @@ const styles = StyleSheet.create({
107
146
  textCenter: {
108
147
  alignSelf: 'center',
109
148
  },
149
+ columnFlex: {
150
+ paddingLeft: 16,
151
+ justifyContent: 'center',
152
+ flexDirection: 'column',
153
+ },
154
+ rowFlex: {
155
+ flexDirection: 'row',
156
+ },
157
+ endFlex: {
158
+ flex: 1,
159
+ justifyContent: 'flex-start',
160
+ alignItems: 'flex-end',
161
+ paddingRight: 16,
162
+ },
163
+ Border: {
164
+ display: 'flex',
165
+ width: '100%',
166
+ borderWidth: 0,
167
+ borderColor: Colors.Gray4,
168
+ borderBottomWidth: 1,
169
+ borderStyle: 'solid',
170
+ flexDirection: 'row',
171
+ paddingTop: 16,
172
+ paddingBottom: 16,
173
+ },
174
+ titleName: {
175
+ fontSize: 16,
176
+ lineHeight: 24,
177
+ fontStyle: 'normal',
178
+ fontWeight: '400',
179
+ color: Colors.Gray9,
180
+ },
181
+ status: {
182
+ fontSize: 12,
183
+ lineHeight: 20,
184
+ fontStyle: 'normal',
185
+ fontWeight: '400',
186
+ color: Colors.Gray7,
187
+ },
188
+ container: {
189
+ paddingTop: 0,
190
+ },
191
+ paddingLeft16: {
192
+ paddingLeft: 16,
193
+ },
194
+ avatar: {
195
+ height: 40,
196
+ width: 40,
197
+ borderRadius: 40,
198
+ backgroundColor: Colors.Primary,
199
+ },
110
200
  });
@@ -27,7 +27,7 @@ describe('MemberList', () => {
27
27
  });
28
28
  const instance = tree.root;
29
29
  const textInputs = instance.findAllByType(Text);
30
- expect(textInputs.length).toBe(4);
30
+ expect(textInputs.length).toBe(5);
31
31
  });
32
32
  test('MemberList snapshot id dataMember !== ownerId', () => {
33
33
  const dataMember = [{ id: 1, name: 'CEO' }];
@@ -36,7 +36,7 @@ describe('MemberList', () => {
36
36
  });
37
37
  const instance = tree.root;
38
38
  const textInputs = instance.findAllByType(Text);
39
- expect(textInputs.length).toBe(3);
39
+ expect(textInputs.length).toBe(5);
40
40
  });
41
41
  test('MemberList snapshot id dataMember === currentUserId', () => {
42
42
  const dataMember = [{ id: 1, name: 'CEO' }];
@@ -45,7 +45,7 @@ describe('MemberList', () => {
45
45
  });
46
46
  const instance = tree.root;
47
47
  const textInputs = instance.findAllByType(Text);
48
- expect(textInputs.length).toBe(4);
48
+ expect(textInputs.length).toBe(5);
49
49
  });
50
50
  test('MemberList dataMember null', () => {
51
51
  const dataMember = [];
@@ -26,8 +26,8 @@ describe('RowMember', () => {
26
26
  });
27
27
  const instance = tree.root;
28
28
  const textInputs = instance.findAllByType(Text);
29
- expect(textInputs.length).toBe(4);
30
- expect(textInputs[0].props.children).toEqual('CEO');
29
+ expect(textInputs.length).toBe(5);
30
+ expect(textInputs[1].props.children).toEqual('CEO');
31
31
  });
32
32
  test('RowMember owner dont have name show start of email ', () => {
33
33
  const dataMember = { id: 1, name: '', email: 'abc@gmail.com' };
@@ -36,7 +36,7 @@ describe('RowMember', () => {
36
36
  });
37
37
  const instance = tree.root;
38
38
  const textInputs = instance.findAllByType(Text);
39
- expect(textInputs.length).toBe(4);
40
- expect(textInputs[0].props.children).toEqual('abc');
39
+ expect(textInputs.length).toBe(5);
40
+ expect(textInputs[1].props.children).toEqual('abc');
41
41
  });
42
42
  });
@@ -39,6 +39,8 @@ const API = {
39
39
  SCConfig.apiRoot + `/property_manager/units/${id}/add_gateway/`,
40
40
  SENSORS_STATUS: (id) =>
41
41
  SCConfig.apiRoot + `/property_manager/units/${id}/sensors_status/`,
42
+ CHANGE_OWNER: (id) =>
43
+ SCConfig.apiRoot + `/property_manager/units/${id}/change_owner/`,
42
44
  FAVOURITE_DEVICES: (id) =>
43
45
  SCConfig.apiRoot + `/property_manager/units/${id}/favourite_devices/`,
44
46
  },
@@ -150,6 +152,12 @@ const API = {
150
152
  UNITS_MEMBER_DETAIL: (id, shareId) =>
151
153
  SCConfig.apiRoot +
152
154
  `/property_manager/sharing/units/${id}/members/${shareId}/`,
155
+ UNIT_MEMBER_INFO: (unit_id, member_id) =>
156
+ SCConfig.apiRoot +
157
+ `/property_manager/sharing/units/${unit_id}/member/${member_id}/information/`,
158
+ UNIT_MEMBER_SHARE_DEVICE: (unit_id, member_id) =>
159
+ SCConfig.apiRoot +
160
+ `/property_manager/sharing/units/${unit_id}/member/${member_id}/shared_devices/`,
153
161
  SHARE: () => SCConfig.apiRoot + '/property_manager/sharing/share/',
154
162
  },
155
163
  EMERGENCY: {
@@ -157,6 +165,8 @@ const API = {
157
165
  },
158
166
  EMERGENCY_BUTTON: {
159
167
  CREATE_CONTACT: () => SCConfig.apiRoot + '/emergency_button/contacts/',
168
+ CREATE_BATCH: () =>
169
+ SCConfig.apiRoot + '/emergency_button/contacts/create_batch/',
160
170
  CONTACTS: () => SCConfig.apiRoot + '/emergency_button/contacts/',
161
171
  REMOVE_CONTACTS: (id) =>
162
172
  SCConfig.apiRoot + `/emergency_button/contacts/${id}/`,