@eohjsc/react-native-smart-city 0.2.61 → 0.2.65
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/README.md +115 -68
- package/package.json +2 -2
- package/src/commons/ActionGroup/CurtainButtonTemplate.js +10 -2
- package/src/commons/ActionGroup/__test__/CurtainButtonTemplate.test.js +1 -1
- package/src/commons/ActionGroup/__test__/MenuActionAddSchedule.test.js +71 -0
- package/src/commons/ActionGroup/hooks/AccessScheduleDetailStyles.js +41 -0
- package/src/commons/ActionGroup/hooks/MenuActionAddSchedule.js +110 -0
- package/src/commons/ActionGroup/hooks/MenuActionAddScheduleStyle.js +69 -0
- package/src/commons/ActionGroup/hooks/RecurringDetail.js +97 -0
- package/src/commons/DateTimeRangeChange/DateTimeButton.js +6 -1
- package/src/commons/Device/HistoryChart.js +2 -1
- package/src/commons/Device/HorizontalBarChart.js +2 -1
- package/src/commons/Device/LinearChart.js +28 -1
- package/src/commons/Form/CurrencyInput.js +1 -0
- package/src/commons/SubUnit/OneTap/OneTapStyles.js +20 -1
- package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +151 -40
- package/src/commons/SubUnit/OneTap/index.js +64 -12
- package/src/configs/Constants.js +9 -0
- package/src/iot/RemoteControl/Bluetooth.js +6 -3
- package/src/iot/RemoteControl/GoogleHome.js +5 -2
- package/src/iot/RemoteControl/Internet.js +1 -0
- package/src/iot/RemoteControl/LG.js +2 -1
- package/src/iot/RemoteControl/index.js +13 -6
- package/src/navigations/SharedStack.js +8 -4
- package/src/navigations/UnitStack.js +10 -2
- package/src/screens/ActivityLog/hooks/index.js +1 -1
- package/src/screens/AddNewAction/SelectSensorDevices.js +18 -11
- package/src/screens/AddNewAction/Styles/SelectSensorDevicesStyles.js +5 -1
- package/src/screens/AddNewAction/__test__/SelectSensorDevices.test.js +6 -1
- package/src/screens/AddNewGateway/AddNewGatewayStyles.js +6 -1
- package/src/screens/AllCamera/index.js +27 -5
- package/src/screens/Automate/MultiUnits.js +7 -4
- package/src/screens/Automate/__test__/MultiUnits.test.js +1 -1
- package/src/screens/Automate/__test__/index.test.js +12 -0
- package/src/screens/ConfirmUnitDeletion/__test__/ConfirmUnitDeletion.test.js +61 -0
- package/src/screens/ConfirmUnitDeletion/index.js +64 -0
- package/src/screens/ConfirmUnitDeletion/styles.js +37 -0
- package/src/screens/EmergencySetting/index.js +2 -0
- package/src/screens/EmergencySetting/styles.js +8 -2
- package/src/screens/ScriptDetail/Styles/indexStyles.js +3 -0
- package/src/screens/ScriptDetail/index.js +7 -1
- package/src/screens/SubUnit/AddSubUnit.js +13 -2
- package/src/screens/SubUnit/EditSubUnit.js +8 -1
- package/src/screens/SubUnit/EditSubUnitStyles.js +2 -3
- package/src/screens/SubUnit/__test__/AddSubUnit.test.js +2 -1
- package/src/screens/TDSGuide/index.js +1 -1
- package/src/screens/Unit/Detail.js +20 -7
- package/src/screens/Unit/ManageUnit.js +9 -12
- package/src/screens/Unit/SmartAccount.js +25 -41
- package/src/screens/Unit/SmartAccountItem.js +2 -1
- package/src/screens/Unit/SmartAccountStyles.js +0 -1
- package/src/screens/Unit/__test__/ManageUnit.test.js +0 -6
- package/src/screens/Unit/__test__/SmartAccount.test.js +24 -0
- package/src/screens/Unit/__test__/SmartAccountItem.test.js +72 -0
- package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +2 -1
- package/src/screens/UnitSummary/components/PowerConsumption/index.js +1 -1
- package/src/screens/UnitSummary/components/Temperature/ItemTemperature/index.js +2 -2
- package/src/screens/WaterQualityGuide/index.js +2 -2
- package/src/utils/I18n/translations/en.json +15 -3
- package/src/utils/I18n/translations/vi.json +15 -3
- package/src/utils/Route/index.js +1 -0
- package/src/utils/Utils.js +4 -0
package/README.md
CHANGED
|
@@ -1,101 +1,148 @@
|
|
|
1
|
-
|
|
1
|
+
## react-native-smart-city
|
|
2
2
|
|
|
3
3
|
## Getting started
|
|
4
|
+
1. Install package dependencies:
|
|
5
|
+
- Using [Yarn](https://yarnpkg.com/): `yarn add react-native-reanimated@^1.10.1`
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
2. Install:
|
|
8
|
+
- Using [npm](https://www.npmjs.com/#getting-started): `npm install @eohjsc/react-native-smart-city --save`
|
|
9
|
+
- Using [Yarn](https://yarnpkg.com/): `yarn add @eohjsc/react-native-smart-city`
|
|
6
10
|
|
|
11
|
+
3. Compile application using react-native run-android
|
|
7
12
|
### Mostly automatic installation
|
|
8
|
-
|
|
9
13
|
`$ react-native link @eohjsc/react-native-smart-city`
|
|
10
14
|
|
|
11
15
|
## Usage
|
|
12
|
-
|
|
13
|
-
Make sure that SmartCity
|
|
14
|
-
Example:
|
|
15
|
-
|
|
16
|
+
1. StackNavigator
|
|
16
17
|
```javascript
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
EmergencyContactsStack,
|
|
18
|
+
import {
|
|
19
|
+
UnitStack,
|
|
20
20
|
AddMemberStack,
|
|
21
|
-
|
|
21
|
+
AddSubUnitStack,
|
|
22
22
|
AddDeviceStack,
|
|
23
|
-
|
|
24
|
-
AddUnitStack,
|
|
25
|
-
SharedStack,
|
|
26
|
-
Explore,
|
|
27
|
-
SCProvider,
|
|
23
|
+
AddGatewayStack,
|
|
28
24
|
SCContext,
|
|
29
25
|
AddLGDeviceStack,
|
|
30
26
|
initSCConfig,
|
|
27
|
+
AddUnitStack,
|
|
31
28
|
AutomateStack,
|
|
32
29
|
NotificationStack,
|
|
33
|
-
MyPinnedSharedUnit,
|
|
34
|
-
SharedUnit,
|
|
35
|
-
MyUnit,
|
|
36
30
|
} from '@eohjsc/react-native-smart-city';
|
|
37
31
|
import { createStackNavigator } from '@react-navigation/stack';
|
|
38
32
|
|
|
39
33
|
// TODO: What to do with the module?
|
|
40
|
-
const Stack =
|
|
34
|
+
const Stack = createStackNavigator();
|
|
41
35
|
|
|
42
36
|
const YourStack = () => {
|
|
43
37
|
// Declare yourAuthObject and params
|
|
44
38
|
return (
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/>
|
|
83
|
-
<Stack.Screen
|
|
84
|
-
name={'EmergencyContactsStack'}
|
|
85
|
-
component={EmergencyContactsStack}
|
|
86
|
-
options={{ headerShown: false }}
|
|
87
|
-
/>
|
|
88
|
-
</Stack.Navigator>
|
|
89
|
-
</NavigationContainer>
|
|
90
|
-
</SmartCity>
|
|
39
|
+
<Stack.Navigator>
|
|
40
|
+
<Stack.Screen
|
|
41
|
+
name={'MainTab'}
|
|
42
|
+
component={MainTab}
|
|
43
|
+
options={{
|
|
44
|
+
headerShown: false,
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
<Stack.Screen
|
|
48
|
+
name={'UnitStack'}
|
|
49
|
+
component={UnitStack}
|
|
50
|
+
initialParams={params}
|
|
51
|
+
options={{
|
|
52
|
+
headerShown: false,
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
<Stack.Screen
|
|
56
|
+
name={'AddGatewayStack'}
|
|
57
|
+
component={AddGatewayStack}
|
|
58
|
+
options={{ headerShown: false }}
|
|
59
|
+
/>
|
|
60
|
+
<Stack.Screen
|
|
61
|
+
name={'AddDeviceStack'}
|
|
62
|
+
component={AddDeviceStack}
|
|
63
|
+
options={{ headerShown: false }}
|
|
64
|
+
/>
|
|
65
|
+
<Stack.Screen
|
|
66
|
+
name={'AddMemberStack'}
|
|
67
|
+
component={AddMemberStack}
|
|
68
|
+
options={{ headerShown: false }}
|
|
69
|
+
/>
|
|
70
|
+
<Stack.Screen
|
|
71
|
+
name={'AddSubUnitStack'}
|
|
72
|
+
component={AddSubUnitStack}
|
|
73
|
+
options={{ headerShown: false }}
|
|
74
|
+
/>
|
|
75
|
+
</Stack.Navigator>
|
|
91
76
|
);
|
|
92
77
|
};
|
|
93
78
|
const MainTab = () => {
|
|
94
79
|
return (
|
|
95
80
|
<Tab.Navigator>
|
|
96
|
-
<Tab.Screen name={'
|
|
97
|
-
<Tab.Screen name={'
|
|
81
|
+
<Tab.Screen name={'AutomateStack'} component={AutomateStack} />
|
|
82
|
+
<Tab.Screen name={'NotificationStack'} component={NotificationStack} />
|
|
98
83
|
</Tab.Navigator>
|
|
99
84
|
);
|
|
100
85
|
};
|
|
101
86
|
```
|
|
87
|
+
2. Use components
|
|
88
|
+
```javascript
|
|
89
|
+
import React from 'react';
|
|
90
|
+
import { View } from 'react-native';
|
|
91
|
+
import { MyPinnedSharedUnit, MyUnit } from '@eohjsc/react-native-smart-city';
|
|
92
|
+
|
|
93
|
+
const MyScreen = () => {
|
|
94
|
+
return (
|
|
95
|
+
<View>
|
|
96
|
+
<MyUnit />
|
|
97
|
+
<MyPinnedSharedUnit />
|
|
98
|
+
</View>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
```
|
|
102
|
+
3. Trigger quick action
|
|
103
|
+
```javascript
|
|
104
|
+
import React from 'react';
|
|
105
|
+
import { View, Button } from 'react-native';
|
|
106
|
+
import { sendRemoteCommand } from '@eohjsc/react-native-smart-city/src/iot/RemoteControl';
|
|
107
|
+
|
|
108
|
+
export const MyFunctionalComponent = () => {
|
|
109
|
+
const sensor = {
|
|
110
|
+
id: 1,
|
|
111
|
+
remote_control_options: {
|
|
112
|
+
internet: {},
|
|
113
|
+
bluetooth: {},
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
const action = {
|
|
117
|
+
color: '#00979D',
|
|
118
|
+
command_prefer_over_bluetooth: true,
|
|
119
|
+
command_prefer_over_googlehome: false,
|
|
120
|
+
command_prefer_over_internet: false,
|
|
121
|
+
googlehome_actions: [],
|
|
122
|
+
icon: 'caret-up',
|
|
123
|
+
icon_kit: null,
|
|
124
|
+
id: 1,
|
|
125
|
+
key: '24240de0-5c8e-11ec-bf63-0242ac130002',
|
|
126
|
+
lg_actions: [],
|
|
127
|
+
name: 'Garage Up',
|
|
128
|
+
};
|
|
129
|
+
const handleQuickAction = async() => {
|
|
130
|
+
const result = await sendRemoteCommand(sensor, action); // Action Garage Up
|
|
131
|
+
if (result){
|
|
132
|
+
console.log('Successful control')
|
|
133
|
+
}else {
|
|
134
|
+
console.log('Control failed')
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
return (
|
|
138
|
+
<View>
|
|
139
|
+
<Button onPress={handleQuickAction} title="Example trigger quick action control Garage Up" />
|
|
140
|
+
</View>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
- sendRemoteCommand user needs to `have permission to control the device`, you pass in the `sensor and action` to it and it will run after calling render.
|
|
145
|
+
- `sensor` is a device in your Unit and action is the `action` key of the device..
|
|
146
|
+
- You can take out the sensor and action from your Unit when calling the `API.UNIT.UNIT_DETAIL(unitId)` it will return all devices in 1 unit.
|
|
147
|
+
- Or get sensor from `API.SENSOR.DISPLAY(sensor.id)` returns all actions of 1 device.
|
|
148
|
+
- Refer YourProjectPath /node_modules/@eohjsc/react-native-smart-city/src/screens/Unit/Detail.js
|
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.
|
|
4
|
+
"version": "0.2.65",
|
|
5
5
|
"description": "TODO",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"@react-native-community/async-storage": "^1.12.1",
|
|
107
107
|
"@react-native-community/cameraroll": "^4.0.0",
|
|
108
108
|
"@react-native-community/checkbox": "^0.5.5",
|
|
109
|
-
"@react-native-community/datetimepicker": "
|
|
109
|
+
"@react-native-community/datetimepicker": "https://github.com/hinh-eoh/datepicker",
|
|
110
110
|
"@react-native-community/geolocation": "^2.0.2",
|
|
111
111
|
"@react-native-community/masked-view": "^0.1.10",
|
|
112
112
|
"@react-native-community/netinfo": "^6.0.0",
|
|
@@ -8,11 +8,14 @@ import { TESTID } from '../../configs/Constants';
|
|
|
8
8
|
import { Colors, Images } from '../../configs';
|
|
9
9
|
import { Section } from '../../commons/Section';
|
|
10
10
|
import { IconOutline } from '@ant-design/icons-react-native';
|
|
11
|
+
import MenuActionAddSchedule from './hooks/MenuActionAddSchedule';
|
|
12
|
+
import { useBoolean } from '../../hooks/Common';
|
|
11
13
|
|
|
12
14
|
const CurtainButtonTemplate = memo(({ actionGroup, doAction }) => {
|
|
13
15
|
const t = useTranslations();
|
|
14
16
|
const { configuration } = actionGroup;
|
|
15
17
|
const [lock, setLock] = useState(false);
|
|
18
|
+
const [isShowModal, setShowModal, setHideModal] = useBoolean(false);
|
|
16
19
|
|
|
17
20
|
const onButtonOpenPress = useCallback(
|
|
18
21
|
() => doAction(configuration.open_action_data),
|
|
@@ -120,17 +123,22 @@ const CurtainButtonTemplate = memo(({ actionGroup, doAction }) => {
|
|
|
120
123
|
<Text type="H4" color={Colors.Gray8}>
|
|
121
124
|
{t('schedule')}
|
|
122
125
|
</Text>
|
|
123
|
-
<TouchableOpacity>
|
|
126
|
+
<TouchableOpacity onPress={setShowModal}>
|
|
124
127
|
<IconOutline name="plus" size={20} />
|
|
125
128
|
</TouchableOpacity>
|
|
126
129
|
</View>
|
|
127
130
|
<View>
|
|
128
|
-
<TouchableOpacity
|
|
131
|
+
<TouchableOpacity
|
|
132
|
+
onPress={setShowModal}
|
|
133
|
+
style={styles.tapToAddSchedule}
|
|
134
|
+
>
|
|
129
135
|
<Text type="Body" color={Colors.Gray6} center>
|
|
130
136
|
{t('tap_to_add_new_schedule')}
|
|
131
137
|
</Text>
|
|
132
138
|
</TouchableOpacity>
|
|
133
139
|
</View>
|
|
140
|
+
|
|
141
|
+
<MenuActionAddSchedule visible={isShowModal} hideModal={setHideModal} />
|
|
134
142
|
</Section>
|
|
135
143
|
</>
|
|
136
144
|
);
|
|
@@ -67,6 +67,6 @@ describe('Test CurtainButtonTemplate', () => {
|
|
|
67
67
|
});
|
|
68
68
|
const instance = wrapper.root;
|
|
69
69
|
const touchableOpacities = instance.findAllByType(TouchableOpacity);
|
|
70
|
-
expect(touchableOpacities.length).toEqual(
|
|
70
|
+
expect(touchableOpacities.length).toEqual(18);
|
|
71
71
|
});
|
|
72
72
|
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer, { act } from 'react-test-renderer';
|
|
3
|
+
|
|
4
|
+
import MenuActionAddSchedule from '../hooks/MenuActionAddSchedule';
|
|
5
|
+
import { SCProvider } from '../../../context';
|
|
6
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
7
|
+
import RecurringDetail from '../hooks/RecurringDetail';
|
|
8
|
+
import WheelDateTimePicker from '../../WheelDateTimePicker';
|
|
9
|
+
|
|
10
|
+
const mockSetState = jest.fn();
|
|
11
|
+
jest.mock('react', () => {
|
|
12
|
+
return {
|
|
13
|
+
...jest.requireActual('react'),
|
|
14
|
+
memo: (x) => x,
|
|
15
|
+
useState: jest.fn((init) => [init, mockSetState]),
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const wrapComponent = (actionGroup) => (
|
|
20
|
+
<SCProvider initState={mockSCStore({})}>
|
|
21
|
+
<MenuActionAddSchedule />
|
|
22
|
+
</SCProvider>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const mockedNavigate = jest.fn();
|
|
26
|
+
|
|
27
|
+
jest.mock('@react-navigation/native', () => {
|
|
28
|
+
return {
|
|
29
|
+
...jest.requireActual('@react-navigation/native'),
|
|
30
|
+
useNavigation: () => ({
|
|
31
|
+
goBack: mockedNavigate,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
jest.mock('react', () => {
|
|
37
|
+
return {
|
|
38
|
+
...jest.requireActual('react'),
|
|
39
|
+
memo: (x) => x,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
jest.mock('axios');
|
|
44
|
+
|
|
45
|
+
describe('Test MenuActionAddSchedule', () => {
|
|
46
|
+
let tree;
|
|
47
|
+
|
|
48
|
+
test('test render MenuActionAddSchedule', async () => {
|
|
49
|
+
await act(async () => {
|
|
50
|
+
tree = await renderer.create(wrapComponent());
|
|
51
|
+
});
|
|
52
|
+
const instance = tree.root;
|
|
53
|
+
|
|
54
|
+
const recurringDetail = instance.findByType(RecurringDetail);
|
|
55
|
+
await act(async () => {
|
|
56
|
+
recurringDetail.props.onShowSetDateTime();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const wheelDateTimePicker = instance.findByType(WheelDateTimePicker);
|
|
60
|
+
|
|
61
|
+
await act(async () => {
|
|
62
|
+
wheelDateTimePicker.props.onPicked();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await act(async () => {
|
|
66
|
+
wheelDateTimePicker.props.onCancel();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(wheelDateTimePicker.props.isVisible).toEqual(false);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Colors } from '../../../configs';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
title: {
|
|
6
|
+
marginTop: 16,
|
|
7
|
+
},
|
|
8
|
+
value: {
|
|
9
|
+
marginTop: 8,
|
|
10
|
+
marginBottom: 12,
|
|
11
|
+
},
|
|
12
|
+
repeatWrap: {
|
|
13
|
+
flexDirection: 'row',
|
|
14
|
+
justifyContent: 'space-around',
|
|
15
|
+
marginTop: 16,
|
|
16
|
+
},
|
|
17
|
+
repeatItem: {
|
|
18
|
+
width: 24,
|
|
19
|
+
height: 24,
|
|
20
|
+
justifyContent: 'center',
|
|
21
|
+
alignItems: 'center',
|
|
22
|
+
borderRadius: 12,
|
|
23
|
+
borderWidth: 0,
|
|
24
|
+
borderColor: Colors.Orange,
|
|
25
|
+
},
|
|
26
|
+
repeatItemSelected: {
|
|
27
|
+
borderWidth: 1,
|
|
28
|
+
},
|
|
29
|
+
repeatText: {
|
|
30
|
+
lineHeight: 16,
|
|
31
|
+
},
|
|
32
|
+
curtainText: {
|
|
33
|
+
fontSize: 17,
|
|
34
|
+
marginTop: 12,
|
|
35
|
+
color: Colors.Gray9,
|
|
36
|
+
},
|
|
37
|
+
boder: {
|
|
38
|
+
borderBottomWidth: 1,
|
|
39
|
+
borderColor: Colors.Gray4,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React, { memo, useCallback, useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
4
|
+
|
|
5
|
+
import Text from '../../Text';
|
|
6
|
+
import { ModalCustom } from '../../Modal';
|
|
7
|
+
import ViewButtonBottom from '../../ViewButtonBottom';
|
|
8
|
+
import WheelDateTimePicker from '../../WheelDateTimePicker';
|
|
9
|
+
import moment from 'moment';
|
|
10
|
+
import { useBoolean } from '../../../hooks/Common';
|
|
11
|
+
import RecurringDetail from './RecurringDetail';
|
|
12
|
+
import _TextInput from '../../../commons/Form/TextInput';
|
|
13
|
+
import styles from './MenuActionAddScheduleStyle';
|
|
14
|
+
|
|
15
|
+
const MenuActionAddSchedule = memo(({ visible, hideModal, onItemClick }) => {
|
|
16
|
+
const t = useTranslations();
|
|
17
|
+
|
|
18
|
+
const [stateDateTimePicker, setStateDateTimePicker] = useState({
|
|
19
|
+
isVisible: false,
|
|
20
|
+
mode: 'time',
|
|
21
|
+
defaultValue: moment().valueOf(),
|
|
22
|
+
setter: null,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const [lockShowing, releaseLockShowing, acquireLockShowing] =
|
|
26
|
+
useBoolean(true);
|
|
27
|
+
const [recurringTimeStart, setRecurringTimeStart] = useState('');
|
|
28
|
+
const [recurringTimeEnd, setRecurringTimeEnd] = useState('');
|
|
29
|
+
const [recurringTimeRepeat, setRecurringTimeRepeat] = useState('');
|
|
30
|
+
|
|
31
|
+
const onShowSetDateTime = useCallback(
|
|
32
|
+
(currentValue, setter, mode) => {
|
|
33
|
+
acquireLockShowing();
|
|
34
|
+
setStateDateTimePicker((state) => ({
|
|
35
|
+
...state,
|
|
36
|
+
isVisible: true,
|
|
37
|
+
mode: mode,
|
|
38
|
+
defaultValue: currentValue,
|
|
39
|
+
setter: setter,
|
|
40
|
+
}));
|
|
41
|
+
},
|
|
42
|
+
[setStateDateTimePicker, acquireLockShowing]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const onDateTimePicked = useCallback(
|
|
46
|
+
(timeData) => {
|
|
47
|
+
const setter = stateDateTimePicker.setter;
|
|
48
|
+
setter && setter(moment(timeData));
|
|
49
|
+
},
|
|
50
|
+
[stateDateTimePicker]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const onHideSetDateTime = useCallback(() => {
|
|
54
|
+
acquireLockShowing();
|
|
55
|
+
setStateDateTimePicker((state) => ({
|
|
56
|
+
...state,
|
|
57
|
+
isVisible: false,
|
|
58
|
+
}));
|
|
59
|
+
}, [setStateDateTimePicker, acquireLockShowing]);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<ModalCustom
|
|
63
|
+
isVisible={visible}
|
|
64
|
+
onBackButtonPress={hideModal}
|
|
65
|
+
onBackdropPress={hideModal}
|
|
66
|
+
style={styles.container}
|
|
67
|
+
>
|
|
68
|
+
<View style={styles.popoverStyle}>
|
|
69
|
+
<View style={styles.modalWrapper}>
|
|
70
|
+
<View style={styles.modalHeader}>
|
|
71
|
+
<Text style={styles.modalHeaderText}>{t('add_schedule')}</Text>
|
|
72
|
+
<View style={styles.boder} />
|
|
73
|
+
<RecurringDetail
|
|
74
|
+
onShowSetDateTime={onShowSetDateTime}
|
|
75
|
+
recurringTimeStart={recurringTimeStart}
|
|
76
|
+
recurringTimeEnd={recurringTimeEnd}
|
|
77
|
+
recurringTimeRepeat={recurringTimeRepeat}
|
|
78
|
+
setRecurringTimeStart={setRecurringTimeStart}
|
|
79
|
+
setRecurringTimeEnd={setRecurringTimeEnd}
|
|
80
|
+
setRecurringTimeRepeat={setRecurringTimeRepeat}
|
|
81
|
+
/>
|
|
82
|
+
|
|
83
|
+
<Text style={styles.subName}>{t('schedule_name')}</Text>
|
|
84
|
+
<_TextInput
|
|
85
|
+
placeholder={t('enter_name')}
|
|
86
|
+
wrapStyle={styles.noMarginTop}
|
|
87
|
+
textInputStyle={styles.textInput}
|
|
88
|
+
/>
|
|
89
|
+
<View style={styles.ViewButtonBottom}>
|
|
90
|
+
<ViewButtonBottom
|
|
91
|
+
leftTitle={t('cancel')}
|
|
92
|
+
onLeftClick={hideModal}
|
|
93
|
+
rightTitle={t('done')}
|
|
94
|
+
/>
|
|
95
|
+
</View>
|
|
96
|
+
<WheelDateTimePicker
|
|
97
|
+
mode={stateDateTimePicker.mode}
|
|
98
|
+
isVisible={stateDateTimePicker.isVisible && lockShowing}
|
|
99
|
+
defaultValue={stateDateTimePicker.defaultValue}
|
|
100
|
+
onPicked={onDateTimePicked}
|
|
101
|
+
onCancel={onHideSetDateTime}
|
|
102
|
+
onHide={releaseLockShowing}
|
|
103
|
+
/>
|
|
104
|
+
</View>
|
|
105
|
+
</View>
|
|
106
|
+
</View>
|
|
107
|
+
</ModalCustom>
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
export default MenuActionAddSchedule;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Colors } from '../../../configs';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
container: {
|
|
6
|
+
flex: 1,
|
|
7
|
+
margin: 0,
|
|
8
|
+
},
|
|
9
|
+
popoverStyle: {
|
|
10
|
+
width: '100%',
|
|
11
|
+
backgroundColor: Colors.White,
|
|
12
|
+
bottom: 0,
|
|
13
|
+
left: 0,
|
|
14
|
+
position: 'absolute',
|
|
15
|
+
borderRadius: 10,
|
|
16
|
+
},
|
|
17
|
+
modalWrapper: {
|
|
18
|
+
flex: 1,
|
|
19
|
+
flexDirection: 'column',
|
|
20
|
+
backgroundColor: Colors.White,
|
|
21
|
+
borderRadius: 10,
|
|
22
|
+
},
|
|
23
|
+
modalHeader: {
|
|
24
|
+
padding: 16,
|
|
25
|
+
backgroundColor: Colors.White,
|
|
26
|
+
justifyContent: 'flex-start',
|
|
27
|
+
},
|
|
28
|
+
modalHeaderText: {
|
|
29
|
+
fontSize: 17,
|
|
30
|
+
lineHeight: 24,
|
|
31
|
+
color: Colors.Gray9,
|
|
32
|
+
fontWeight: 'bold',
|
|
33
|
+
borderTopLeftRadius: 20,
|
|
34
|
+
borderTopRightRadius: 20,
|
|
35
|
+
marginVertical: 20,
|
|
36
|
+
},
|
|
37
|
+
boder: {
|
|
38
|
+
borderBottomWidth: 1,
|
|
39
|
+
borderColor: Colors.Gray4,
|
|
40
|
+
},
|
|
41
|
+
subName: {
|
|
42
|
+
color: Colors.Gray8,
|
|
43
|
+
marginTop: 12,
|
|
44
|
+
},
|
|
45
|
+
ViewButtonBottom: {
|
|
46
|
+
borderTopWidth: 1,
|
|
47
|
+
borderTopColor: Colors.Gray4,
|
|
48
|
+
},
|
|
49
|
+
scheduleName: {
|
|
50
|
+
fontSize: 17,
|
|
51
|
+
marginTop: 12,
|
|
52
|
+
color: Colors.Gray9,
|
|
53
|
+
marginBottom: 12,
|
|
54
|
+
},
|
|
55
|
+
noMarginTop: {
|
|
56
|
+
marginTop: 0,
|
|
57
|
+
backgroundColor: Colors.White,
|
|
58
|
+
},
|
|
59
|
+
textInput: {
|
|
60
|
+
marginTop: 0,
|
|
61
|
+
borderWidth: 0,
|
|
62
|
+
borderBottomWidth: 1,
|
|
63
|
+
borderBottomColor: Colors.Gray4,
|
|
64
|
+
paddingLeft: 0,
|
|
65
|
+
backgroundColor: Colors.White,
|
|
66
|
+
fontSize: 17,
|
|
67
|
+
color: Colors.Gray9,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { View, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
4
|
+
|
|
5
|
+
import Text from '../../../commons/Text';
|
|
6
|
+
import { Colors } from '../../../configs';
|
|
7
|
+
import { REPEAT_ITEMS } from '../../../screens/GuestInfo/constant';
|
|
8
|
+
import styles from './AccessScheduleDetailStyles';
|
|
9
|
+
|
|
10
|
+
const RecurringDetail = ({
|
|
11
|
+
onShowSetDateTime,
|
|
12
|
+
recurringTimeStart,
|
|
13
|
+
recurringTimeEnd,
|
|
14
|
+
recurringTimeRepeat,
|
|
15
|
+
setRecurringTimeStart,
|
|
16
|
+
setRecurringTimeEnd,
|
|
17
|
+
setRecurringTimeRepeat,
|
|
18
|
+
}) => {
|
|
19
|
+
const t = useTranslations();
|
|
20
|
+
const onSetTimeStart = useCallback(() => {
|
|
21
|
+
onShowSetDateTime();
|
|
22
|
+
}, [onShowSetDateTime]);
|
|
23
|
+
|
|
24
|
+
const onSetTimeEnd = useCallback(() => {
|
|
25
|
+
onShowSetDateTime();
|
|
26
|
+
}, [onShowSetDateTime]);
|
|
27
|
+
|
|
28
|
+
const onSetRepeat = useCallback(
|
|
29
|
+
(item) => {
|
|
30
|
+
const index = recurringTimeRepeat.indexOf(item.value);
|
|
31
|
+
if (index !== -1) {
|
|
32
|
+
setRecurringTimeRepeat([
|
|
33
|
+
...recurringTimeRepeat.slice(0, index),
|
|
34
|
+
...recurringTimeRepeat.slice(index + 1),
|
|
35
|
+
]);
|
|
36
|
+
} else {
|
|
37
|
+
setRecurringTimeRepeat([...recurringTimeRepeat, item.value]);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
[recurringTimeRepeat, setRecurringTimeRepeat]
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const renderRepeatItem = useCallback(
|
|
44
|
+
(item, index, isSelected) => {
|
|
45
|
+
return (
|
|
46
|
+
<TouchableOpacity
|
|
47
|
+
key={index}
|
|
48
|
+
style={[styles.repeatItem, isSelected && styles.repeatItemSelected]}
|
|
49
|
+
onPress={() => onSetRepeat(item)}
|
|
50
|
+
>
|
|
51
|
+
<Text
|
|
52
|
+
type="Body"
|
|
53
|
+
lineHeight={16}
|
|
54
|
+
color={isSelected ? Colors.Orange : item.color}
|
|
55
|
+
style={styles.repeatText}
|
|
56
|
+
>
|
|
57
|
+
{item.text}
|
|
58
|
+
</Text>
|
|
59
|
+
</TouchableOpacity>
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
[onSetRepeat]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<View>
|
|
67
|
+
<Text style={styles.curtainText}>{t('curtain_opens')}</Text>
|
|
68
|
+
<TouchableOpacity onPress={onSetTimeStart}>
|
|
69
|
+
<Text type="Body" color={Colors.Orange} style={styles.value}>
|
|
70
|
+
07:00 AM
|
|
71
|
+
</Text>
|
|
72
|
+
</TouchableOpacity>
|
|
73
|
+
<View style={styles.boder} />
|
|
74
|
+
<Text style={styles.curtainText}>{t('curtain_closes')}</Text>
|
|
75
|
+
<TouchableOpacity onPress={onSetTimeEnd}>
|
|
76
|
+
<Text type="Body" color={Colors.Orange} style={styles.value}>
|
|
77
|
+
12:00 AM
|
|
78
|
+
</Text>
|
|
79
|
+
</TouchableOpacity>
|
|
80
|
+
<View style={styles.boder} />
|
|
81
|
+
<Text type="Body" color={Colors.Gray8} style={styles.title}>
|
|
82
|
+
{t('repeat')}
|
|
83
|
+
</Text>
|
|
84
|
+
<View style={styles.repeatWrap}>
|
|
85
|
+
{REPEAT_ITEMS.map((item, index) =>
|
|
86
|
+
renderRepeatItem(
|
|
87
|
+
item,
|
|
88
|
+
index,
|
|
89
|
+
recurringTimeRepeat.includes(item.value)
|
|
90
|
+
)
|
|
91
|
+
)}
|
|
92
|
+
</View>
|
|
93
|
+
</View>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default RecurringDetail;
|
|
@@ -16,7 +16,7 @@ const DateTimeButton = memo(({ onPress, time, date, formatType, style }) => {
|
|
|
16
16
|
<Text type={'Label'} color={Colors.Gray8} style={styles.txtTime}>
|
|
17
17
|
{moment(time).format(format)}
|
|
18
18
|
</Text>
|
|
19
|
-
<IconOutline name={'calendar'}
|
|
19
|
+
<IconOutline style={styles.iconDateTime} name={'calendar'} />
|
|
20
20
|
</TouchableOpacity>
|
|
21
21
|
);
|
|
22
22
|
});
|
|
@@ -40,4 +40,9 @@ const styles = StyleSheet.create({
|
|
|
40
40
|
txtTime: {
|
|
41
41
|
marginRight: 4,
|
|
42
42
|
},
|
|
43
|
+
iconDateTime: {
|
|
44
|
+
marginBottom: 4,
|
|
45
|
+
fontSize: 16,
|
|
46
|
+
color: Colors.Gray8,
|
|
47
|
+
},
|
|
43
48
|
});
|