@plusscommunities/pluss-maintenance-app-forms 6.0.8-beta.0

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 (90) hide show
  1. package/dist/module/actions/JobActions.js +20 -0
  2. package/dist/module/actions/JobActions.js.map +1 -0
  3. package/dist/module/actions/index.js +2 -0
  4. package/dist/module/actions/index.js.map +1 -0
  5. package/dist/module/actions/types.js +5 -0
  6. package/dist/module/actions/types.js.map +1 -0
  7. package/dist/module/apis/index.js +2 -0
  8. package/dist/module/apis/index.js.map +1 -0
  9. package/dist/module/apis/maintenanceActions.js +171 -0
  10. package/dist/module/apis/maintenanceActions.js.map +1 -0
  11. package/dist/module/components/FilterPopupMenu.js +271 -0
  12. package/dist/module/components/FilterPopupMenu.js.map +1 -0
  13. package/dist/module/components/MaintenanceList.js +360 -0
  14. package/dist/module/components/MaintenanceList.js.map +1 -0
  15. package/dist/module/components/MaintenanceListItem.js +322 -0
  16. package/dist/module/components/MaintenanceListItem.js.map +1 -0
  17. package/dist/module/components/MaintenanceWidgetItem.js +149 -0
  18. package/dist/module/components/MaintenanceWidgetItem.js.map +1 -0
  19. package/dist/module/components/StatusSelectorPopup.js +89 -0
  20. package/dist/module/components/StatusSelectorPopup.js.map +1 -0
  21. package/dist/module/components/WidgetLarge.js +9 -0
  22. package/dist/module/components/WidgetLarge.js.map +1 -0
  23. package/dist/module/components/WidgetSmall.js +171 -0
  24. package/dist/module/components/WidgetSmall.js.map +1 -0
  25. package/dist/module/core.config.js +17 -0
  26. package/dist/module/core.config.js.map +1 -0
  27. package/dist/module/feature.config.js +75 -0
  28. package/dist/module/feature.config.js.map +1 -0
  29. package/dist/module/helper.js +33 -0
  30. package/dist/module/helper.js.map +1 -0
  31. package/dist/module/images/speechbubble.png +0 -0
  32. package/dist/module/index.js +25 -0
  33. package/dist/module/index.js.map +1 -0
  34. package/dist/module/reducers/JobsReducer.js +63 -0
  35. package/dist/module/reducers/JobsReducer.js.map +1 -0
  36. package/dist/module/screens/JobTypePicker.js +130 -0
  37. package/dist/module/screens/JobTypePicker.js.map +1 -0
  38. package/dist/module/screens/MaintenancePage.js +92 -0
  39. package/dist/module/screens/MaintenancePage.js.map +1 -0
  40. package/dist/module/screens/RequestDetail.js +980 -0
  41. package/dist/module/screens/RequestDetail.js.map +1 -0
  42. package/dist/module/screens/RequestNotes.js +390 -0
  43. package/dist/module/screens/RequestNotes.js.map +1 -0
  44. package/dist/module/screens/ServiceRequest.js +1243 -0
  45. package/dist/module/screens/ServiceRequest.js.map +1 -0
  46. package/dist/module/values.config.a.js +30 -0
  47. package/dist/module/values.config.a.js.map +1 -0
  48. package/dist/module/values.config.b.js +30 -0
  49. package/dist/module/values.config.b.js.map +1 -0
  50. package/dist/module/values.config.c.js +30 -0
  51. package/dist/module/values.config.c.js.map +1 -0
  52. package/dist/module/values.config.d.js +30 -0
  53. package/dist/module/values.config.d.js.map +1 -0
  54. package/dist/module/values.config.default.js +35 -0
  55. package/dist/module/values.config.default.js.map +1 -0
  56. package/dist/module/values.config.forms.js +35 -0
  57. package/dist/module/values.config.forms.js.map +1 -0
  58. package/dist/module/values.config.js +35 -0
  59. package/dist/module/values.config.js.map +1 -0
  60. package/package.json +53 -0
  61. package/src/actions/JobActions.js +22 -0
  62. package/src/actions/index.js +1 -0
  63. package/src/actions/types.js +5 -0
  64. package/src/apis/index.js +1 -0
  65. package/src/apis/maintenanceActions.js +163 -0
  66. package/src/components/FilterPopupMenu.js +256 -0
  67. package/src/components/MaintenanceList.js +335 -0
  68. package/src/components/MaintenanceListItem.js +289 -0
  69. package/src/components/MaintenanceWidgetItem.js +132 -0
  70. package/src/components/StatusSelectorPopup.js +87 -0
  71. package/src/components/WidgetLarge.js +10 -0
  72. package/src/components/WidgetSmall.js +152 -0
  73. package/src/core.config.js +5 -0
  74. package/src/feature.config.js +73 -0
  75. package/src/helper.js +39 -0
  76. package/src/images/speechbubble.png +0 -0
  77. package/src/index.js +25 -0
  78. package/src/reducers/JobsReducer.js +51 -0
  79. package/src/screens/JobTypePicker.js +107 -0
  80. package/src/screens/MaintenancePage.js +96 -0
  81. package/src/screens/RequestDetail.js +915 -0
  82. package/src/screens/RequestNotes.js +418 -0
  83. package/src/screens/ServiceRequest.js +1219 -0
  84. package/src/values.config.a.js +30 -0
  85. package/src/values.config.b.js +30 -0
  86. package/src/values.config.c.js +30 -0
  87. package/src/values.config.d.js +30 -0
  88. package/src/values.config.default.js +35 -0
  89. package/src/values.config.forms.js +35 -0
  90. package/src/values.config.js +35 -0
@@ -0,0 +1,132 @@
1
+ import React, { Component } from 'react';
2
+ import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
3
+ import { connect } from 'react-redux';
4
+ import { Icon } from 'react-native-elements';
5
+ import moment from 'moment';
6
+ import _ from 'lodash';
7
+ import { getJobStatusProps, jobStatusOptions } from '../helper';
8
+ import { Services } from '../feature.config';
9
+ import { Colours, Helper } from '../core.config';
10
+ import { values } from '../values.config';
11
+
12
+ class MaintenanceWidgetItem extends Component {
13
+ onPressJob = () => {
14
+ Services.navigation.navigate(values.screenRequestDetail, { job: this.props.job });
15
+ };
16
+
17
+ render() {
18
+ const { job } = this.props;
19
+ const createdTime = moment(job.createdUnix);
20
+ const createdTimeText = `${createdTime.format('ddd, D MMMM')} • ${createdTime.format('h:mma')}`;
21
+ const { statusText, statusColor } = getJobStatusProps(job.status);
22
+ const seenText = (() => {
23
+ if (!job.status || job.status === jobStatusOptions[0].name) {
24
+ return job.seen ? 'Seen' : 'Unseen';
25
+ }
26
+ return '';
27
+ })();
28
+
29
+ return (
30
+ <TouchableOpacity key={job.id} onPress={this.onPressJob}>
31
+ <View style={styles.jobContainer}>
32
+ <View style={styles.jobInnerContainer}>
33
+ <View style={styles.jobTopSection}>
34
+ <Text numberOfLines={2} style={styles.jobTitleText}>
35
+ {job.title}
36
+ </Text>
37
+ <Text style={styles.jobCreatedTimeText}>{createdTimeText}</Text>
38
+ <View style={styles.jobSeenContainer}>
39
+ {job.seen && !_.isEmpty(seenText) && (
40
+ <Icon name="check" type="font-awesome" iconStyle={[styles.jobSeenIcon, { color: this.props.colourBrandingMain }]} />
41
+ )}
42
+ <Text style={[styles.jobSeenText, job.seen && { color: this.props.colourBrandingMain }]}>{seenText}</Text>
43
+ </View>
44
+ </View>
45
+ <View style={styles.jobBottomSection}>
46
+ <View style={[styles.jobStatusContainer, { backgroundColor: statusColor }]}>
47
+ {/* <Icon name="wrench" type="font-awesome" iconStyle={styles.jobStatusIcon} /> */}
48
+ <Text style={styles.jobStatusText}>{statusText}</Text>
49
+ </View>
50
+ </View>
51
+ </View>
52
+ </View>
53
+ </TouchableOpacity>
54
+ );
55
+ }
56
+ }
57
+
58
+ const styles = StyleSheet.create({
59
+ jobContainer: {
60
+ padding: 10,
61
+ },
62
+ jobInnerContainer: {
63
+ width: 140,
64
+ height: 160,
65
+ ...Helper.getShadowStyle(),
66
+ },
67
+ jobTopSection: {
68
+ flex: 1,
69
+ padding: 10,
70
+ paddingTop: 20,
71
+ },
72
+ jobTitleText: {
73
+ flex: 1,
74
+ fontFamily: 'sf-semibold',
75
+ fontSize: 16,
76
+ color: Colours.TEXT_DARK,
77
+ },
78
+ jobCreatedTimeText: {
79
+ fontFamily: 'sf-medium',
80
+ fontSize: 11,
81
+ color: Colours.TEXT_LIGHT,
82
+ paddingTop: 3,
83
+ },
84
+ jobSeenContainer: {
85
+ flexDirection: 'row',
86
+ alignItems: 'center',
87
+ paddingTop: 5,
88
+ },
89
+ jobSeenIcon: {
90
+ fontSize: 12,
91
+ marginRight: 4,
92
+ },
93
+ jobSeenText: {
94
+ fontFamily: 'sf-semibold',
95
+ fontSize: 12,
96
+ color: Colours.TEXT_SUPER_LIGHT,
97
+ },
98
+ jobBottomSection: {
99
+ alignItems: 'center',
100
+ justifyContent: 'center',
101
+ paddingVertical: 10,
102
+ borderTopWidth: 1,
103
+ borderTopColor: Colours.LINEGREY,
104
+ },
105
+ jobStatusContainer: {
106
+ flexDirection: 'row',
107
+ alignItems: 'center',
108
+ justifyContent: 'space-between',
109
+ width: 105,
110
+ height: 30,
111
+ paddingHorizontal: 8,
112
+ borderRadius: 4,
113
+ },
114
+ jobStatusIcon: {
115
+ color: '#fff',
116
+ fontSize: 14,
117
+ },
118
+ jobStatusText: {
119
+ color: '#fff',
120
+ textAlign: 'center',
121
+ fontFamily: 'sf-semibold',
122
+ fontSize: 13,
123
+ },
124
+ });
125
+
126
+ const mapStateToProps = state => {
127
+ return {
128
+ colourBrandingMain: Colours.getMainBrandingColourFromState(state),
129
+ };
130
+ };
131
+
132
+ export default connect(mapStateToProps, {})(MaintenanceWidgetItem);
@@ -0,0 +1,87 @@
1
+ import React, { PureComponent } from 'react';
2
+ import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
3
+ import { connect } from 'react-redux';
4
+ import { jobStatusOptions, getJobStatusColour } from '../helper';
5
+ import { Components, Colours } from '../core.config';
6
+
7
+ class StatusSelectorPopup extends PureComponent {
8
+ render() {
9
+ const { title, filter, includeAll, allText, onClose, onSelect } = this.props;
10
+ let statuses = filter
11
+ ? filter.map(status => {
12
+ return {
13
+ name: status,
14
+ label: getJobStatusLabel(status),
15
+ color: getJobStatusColour(status),
16
+ };
17
+ })
18
+ : jobStatusOptions;
19
+ if (includeAll)
20
+ statuses = [
21
+ {
22
+ name: allText || 'Show All',
23
+ color: this.props.colourBrandingMain,
24
+ },
25
+ ...statuses,
26
+ ];
27
+
28
+ return (
29
+ <Components.MiddlePopup style={[styles.statusPopup, { height: statuses.length * 50 + 40 }]} onClose={onClose}>
30
+ <Text style={styles.statusPopupTitle}>{title || 'Select Status'}</Text>
31
+ <View style={styles.statusPopupOptionsContainer}>
32
+ {statuses.map(status => {
33
+ return (
34
+ <TouchableOpacity key={status.name} onPress={() => onSelect(status.name)}>
35
+ <View style={[styles.jobStatusContainer, { backgroundColor: status.color }]}>
36
+ <Text style={styles.jobStatusText}>{status.label}</Text>
37
+ </View>
38
+ </TouchableOpacity>
39
+ );
40
+ })}
41
+ </View>
42
+ </Components.MiddlePopup>
43
+ );
44
+ }
45
+ }
46
+
47
+ const styles = StyleSheet.create({
48
+ statusPopup: {
49
+ width: 160,
50
+ padding: 16,
51
+ borderRadius: 12,
52
+ },
53
+ statusPopupTitle: {
54
+ fontFamily: 'sf-bold',
55
+ color: Colours.TEXT_DARK,
56
+ fontSize: 18,
57
+ marginBottom: 16,
58
+ },
59
+ statusPopupOptionsContainer: {
60
+ flex: 1,
61
+ justifyContent: 'space-between',
62
+ },
63
+ jobStatusContainer: {
64
+ flexDirection: 'row',
65
+ alignItems: 'center',
66
+ justifyContent: 'space-between',
67
+ width: 105,
68
+ height: 30,
69
+ paddingHorizontal: 8,
70
+ borderRadius: 4,
71
+ },
72
+ jobStatusText: {
73
+ color: '#fff',
74
+ textAlign: 'center',
75
+ fontFamily: 'sf-semibold',
76
+ fontSize: 13,
77
+ flex: 1,
78
+ },
79
+ });
80
+
81
+ const mapStateToProps = state => {
82
+ return {
83
+ colourBrandingMain: Colours.getMainBrandingColourFromState(state),
84
+ };
85
+ };
86
+
87
+ export default connect(mapStateToProps, {})(StatusSelectorPopup);
@@ -0,0 +1,10 @@
1
+ import React, { PureComponent } from 'react';
2
+ import MaintenanceList from './MaintenanceList';
3
+
4
+ class WidgetLarge extends PureComponent {
5
+ render() {
6
+ return <MaintenanceList {...this.props} />;
7
+ }
8
+ }
9
+
10
+ export default WidgetLarge;
@@ -0,0 +1,152 @@
1
+ import React, { Component } from 'react';
2
+ import { Text, View, ScrollView, StyleSheet } from 'react-native';
3
+ import { connect } from 'react-redux';
4
+ import _ from 'lodash';
5
+ import { maintenanceActions } from '../apis';
6
+ import { jobsLoaded } from '../actions';
7
+ import MaintenanceWidgetItem from './MaintenanceWidgetItem';
8
+ import { Services } from '../feature.config';
9
+ import { Colours, Components, Config } from '../core.config';
10
+ import { values } from '../values.config';
11
+
12
+ const MAX_ITEMS = 10;
13
+
14
+ class WidgetSmall extends Component {
15
+ constructor(props) {
16
+ super(props);
17
+ this.state = { loading: false };
18
+ }
19
+
20
+ componentDidMount() {
21
+ this.refresh();
22
+ }
23
+
24
+ componentDidUpdate(prevProps) {
25
+ if (!prevProps.dataUpdated && this.props.dataUpdated) this.refresh();
26
+ }
27
+
28
+ getTitle = () => {
29
+ const { options } = this.props;
30
+ if (options && !_.isEmpty(options.Title)) return options.Title;
31
+ return this.props.strings[`${values.featureKey}_textFeatureTitle`] || values.textFeatureTitle;
32
+ };
33
+
34
+ getEmptyStateText = () => {
35
+ const { options, userCategory } = this.props;
36
+ if (options && !_.isEmpty(options.EmptyText)) return options.EmptyText;
37
+ return userCategory === 'staff' ? values.emptyRequestsStaff : values.emptyRequestsUser;
38
+ };
39
+
40
+ refresh = () => {
41
+ this.onLoadingChanged(true, async () => {
42
+ try {
43
+ const res = await maintenanceActions.getJobsRecursive(this.props.site);
44
+ // console.log('WidgetSmall - refresh', res.data);
45
+ this.props.jobsLoaded(res);
46
+ } catch (error) {
47
+ console.log('refresh error', error);
48
+ } finally {
49
+ this.onLoadingChanged(false);
50
+ }
51
+ });
52
+ };
53
+
54
+ onLoadingChanged = (loading, callback) => {
55
+ this.setState({ loading }, () => {
56
+ if (this.props.onLoadingChanged) this.props.onLoadingChanged(this.state.loading);
57
+ if (callback) callback();
58
+ });
59
+ };
60
+
61
+ onPressAll = () => {
62
+ Services.navigation.navigate(values.screenMaintenance, { options: this.props.options });
63
+ };
64
+
65
+ renderContent() {
66
+ const { jobs } = this.props;
67
+ if (_.isEmpty(jobs)) {
68
+ if (this.state.loading) {
69
+ return (
70
+ <View style={styles.loadingPadding}>
71
+ <Components.LoadingStateWidget height={180} />
72
+ </View>
73
+ );
74
+ }
75
+ return (
76
+ <View style={styles.loadingPadding}>
77
+ <Components.EmptyStateWidget title={this.getEmptyStateText()} height={180} />
78
+ </View>
79
+ );
80
+ }
81
+ return (
82
+ <ScrollView horizontal contentContainerStyle={{ paddingLeft: 6, paddingRight: 8 }} showsHorizontalScrollIndicator={false}>
83
+ {jobs.slice(0, MAX_ITEMS).map(job => {
84
+ return <MaintenanceWidgetItem key={job.id} job={job} />;
85
+ })}
86
+ </ScrollView>
87
+ );
88
+ }
89
+
90
+ render() {
91
+ const { colourBrandingMain } = this.props;
92
+
93
+ return (
94
+ <View style={styles.sectionContainer}>
95
+ <View style={styles.sectionPadding}>
96
+ <View style={styles.sectionHeading}>
97
+ <Text style={styles.sectionTitle}>{this.getTitle()}</Text>
98
+ <Components.InlineButton
99
+ onPress={this.onPressAll}
100
+ color={colourBrandingMain}
101
+ touchableStyle={{ paddingTop: 6 }}
102
+ textStyle={{ color: '#fff' }}
103
+ >
104
+ View All
105
+ </Components.InlineButton>
106
+ </View>
107
+ </View>
108
+ {this.renderContent()}
109
+ </View>
110
+ );
111
+ }
112
+ }
113
+
114
+ const styles = StyleSheet.create({
115
+ sectionContainer: {
116
+ backgroundColor: '#fff',
117
+ paddingTop: 16,
118
+ },
119
+ sectionPadding: {
120
+ paddingHorizontal: 16,
121
+ paddingBottom: 6,
122
+ },
123
+ loadingPadding: {
124
+ paddingHorizontal: 16,
125
+ },
126
+ sectionHeading: {
127
+ marginBottom: 4,
128
+ flexDirection: 'row',
129
+ alignContent: 'flex-start',
130
+ justifyContent: 'space-between',
131
+ },
132
+ sectionTitle: {
133
+ fontFamily: 'sf-bold',
134
+ fontSize: 24,
135
+ color: Colours.TEXT_DARKEST,
136
+ },
137
+ });
138
+
139
+ const mapStateToProps = state => {
140
+ const { user, notifications } = state;
141
+ const jobs = state[values.reducerKey];
142
+ return {
143
+ colourBrandingMain: Colours.getMainBrandingColourFromState(state),
144
+ jobs: _.orderBy(jobs.jobs, ['createdUnix'], ['desc']),
145
+ site: user.site,
146
+ userCategory: user.category,
147
+ dataUpdated: notifications.dataUpdated[values.updateKey],
148
+ strings: state.strings?.config || {},
149
+ };
150
+ };
151
+
152
+ export default connect(mapStateToProps, { jobsLoaded }, null, { forwardRef: true })(WidgetSmall);
@@ -0,0 +1,5 @@
1
+ // import * as PlussCore from '../../pluss-core/src';
2
+ import * as PlussCore from '@plusscommunities/pluss-core-app';
3
+
4
+ const { Apis, Fonts, Actions, ActionTypes, Config, Components, Styles, Session, Helper, Constants, Colours } = PlussCore;
5
+ export { Apis, Fonts, Actions, ActionTypes, Config, Components, Styles, Session, Helper, Constants, Colours };
@@ -0,0 +1,73 @@
1
+ // import * as PlussCore from '../../pluss-core/src';
2
+ import * as PlussCore from '@plusscommunities/pluss-core-app';
3
+ import { values } from './values.config';
4
+
5
+ export const Services = {
6
+ navigation: null,
7
+ };
8
+
9
+ export const BaseComponents = {
10
+ NotificationBell: null,
11
+ };
12
+
13
+ const FeatureConfig = {
14
+ key: values.featureKey,
15
+ aliases: values.aliases,
16
+ title: values.textFeatureTitle,
17
+ gridMenu: {
18
+ icon: values.iconGridMenu,
19
+ viewBox: values.gridViewBox,
20
+ navigate: values.screenMaintenance,
21
+ },
22
+ addMenu: {
23
+ order: values.orderAddMenu,
24
+ icon: values.iconAddMenu,
25
+ title: values.textAddMenuTitle,
26
+ navigate: values.screenServiceRequest,
27
+ visibleExps: { type: 'feature', value: values.featureKey },
28
+ },
29
+ moreMenu: values.hasMoreOption
30
+ ? {
31
+ order: values.orderMoreMenu,
32
+ title: values.textMoreMenuTitle,
33
+ navigate: values.screenMaintenance,
34
+ visibleExps: {
35
+ type: 'and',
36
+ exps: [
37
+ { type: 'notHidden', value: 'maintenanceRequest' },
38
+ { type: 'notUserType', value: 'KIOSK' },
39
+ ],
40
+ },
41
+ }
42
+ : undefined,
43
+ kioskAction: {
44
+ order: values.orderKioskAction,
45
+ icon: values.iconKioskAction,
46
+ title: values.textKioskActionTitle,
47
+ navigate: values.screenServiceRequest,
48
+ },
49
+ hideTabBar: [],
50
+ env: {
51
+ baseStage: '',
52
+ baseAPIUrl: '',
53
+ hasGradientHeader: false,
54
+ defaultProfileImage: '',
55
+ tinyChatDefault: '',
56
+ baseUploadsUrl: '',
57
+ allowMediaDownload: false,
58
+ allowMediaSharing: false,
59
+ awsUploadsBucket: '',
60
+ awsStorageBucket: '',
61
+ preferredSite: '',
62
+ strings: {},
63
+ newEventDefaults: '',
64
+ defaultAllowComments: true,
65
+ },
66
+ init: (environment, navigation, notificationBell) => {
67
+ FeatureConfig.env = environment;
68
+ Services.navigation = navigation;
69
+ BaseComponents.NotificationBell = notificationBell;
70
+ PlussCore.Config.init(environment, navigation);
71
+ },
72
+ };
73
+ export default FeatureConfig;
package/src/helper.js ADDED
@@ -0,0 +1,39 @@
1
+ import { label } from 'aws-amplify';
2
+ import { Colours } from './core.config';
3
+
4
+ const jobStatusOptions = [
5
+ {
6
+ name: 'Unassigned',
7
+ label: 'Open',
8
+ color: Colours.LINEGREY,
9
+ },
10
+ {
11
+ name: 'In Progress',
12
+ label: 'In Progress',
13
+ color: Colours.COLOUR_TEAL,
14
+ },
15
+ {
16
+ name: 'Completed',
17
+ label: 'Completed',
18
+ color: Colours.COLOUR_GREEN_LIGHT,
19
+ },
20
+ ];
21
+
22
+ const getJobStatusColour = status => {
23
+ const option = jobStatusOptions.find(item => item.name === status);
24
+ return option ? option.color : jobStatusOptions[0].color;
25
+ };
26
+
27
+ const getJobStatusLabel = status => {
28
+ const option = jobStatusOptions.find(item => item.name === status);
29
+ return option ? option.label : jobStatusOptions[0].label;
30
+ };
31
+
32
+ const getJobStatusProps = status => {
33
+ const statusText = getJobStatusLabel(status) || jobStatusOptions[0].label;
34
+ const statusColor = getJobStatusColour(statusText);
35
+
36
+ return { statusText, statusColor };
37
+ };
38
+
39
+ export { jobStatusOptions, getJobStatusColour, getJobStatusProps, getJobStatusLabel };
Binary file
package/src/index.js ADDED
@@ -0,0 +1,25 @@
1
+ import MaintenancePage from './screens/MaintenancePage';
2
+ import RequestDetail from './screens/RequestDetail';
3
+ import ServiceRequest from './screens/ServiceRequest';
4
+ import JobTypePicker from './screens/JobTypePicker';
5
+ import RequestNotes from './screens/RequestNotes';
6
+ import JobsReducer from './reducers/JobsReducer';
7
+ import { values } from './values.config';
8
+
9
+ export const Reducers = (() => {
10
+ const reducers = {};
11
+ reducers[values.reducerKey] = JobsReducer;
12
+ return reducers;
13
+ })();
14
+ export const Screens = (() => {
15
+ const screens = {};
16
+ screens[values.screenMaintenance] = MaintenancePage;
17
+ screens[values.screenRequestDetail] = RequestDetail;
18
+ screens[values.screenServiceRequest] = ServiceRequest;
19
+ screens[values.screenJobTypePicker] = JobTypePicker;
20
+ screens[values.screenRequestNotes] = RequestNotes;
21
+ return screens;
22
+ })();
23
+ export { default as Config } from './feature.config';
24
+ export { default as WidgetSmall } from './components/WidgetSmall';
25
+ export { default as WidgetLarge } from './components/WidgetLarge';
@@ -0,0 +1,51 @@
1
+ /* eslint-disable no-param-reassign */
2
+ import _ from 'lodash';
3
+ import { REHYDRATE } from 'redux-persist';
4
+ import { JOBS_LOADED, JOB_ADDED, JOBS_ADDED } from '../actions/types';
5
+ import { ActionTypes } from '../core.config';
6
+ import { values } from '../values.config';
7
+
8
+ const REDUCER_KEY = values.reducerKey;
9
+
10
+ const INITIAL_STATE = {
11
+ jobs: [],
12
+ };
13
+
14
+ export default (state = INITIAL_STATE, action) => {
15
+ let updateJobs = [];
16
+ let index = 0;
17
+
18
+ switch (action.type) {
19
+ case ActionTypes.LOGOUT:
20
+ case ActionTypes.CHANGE_ROLE:
21
+ return INITIAL_STATE;
22
+ case JOBS_LOADED:
23
+ return { ...state, jobs: action.payload.map(job => ({ title: job.title || job.description, ...job })) };
24
+ case JOBS_ADDED:
25
+ updateJobs = action.payload.map(job => ({ title: job.title || job.description, ...job }));
26
+ updateJobs = _.unionWith(updateJobs, state.jobs, (j1, j2) => {
27
+ return j1.id === j2.id;
28
+ });
29
+ return { ...state, jobs: updateJobs };
30
+ case JOB_ADDED:
31
+ updateJobs = [...state.jobs];
32
+ index = updateJobs.findIndex(item => item.id === action.payload.id);
33
+ if (index > -1) {
34
+ updateJobs[index] = action.payload;
35
+ } else {
36
+ updateJobs.push(action.payload);
37
+ }
38
+ return { ...state, jobs: updateJobs };
39
+ case REHYDRATE:
40
+ if (!action.payload) return state;
41
+ if (action.payload[REDUCER_KEY]) {
42
+ if (action.payload[REDUCER_KEY].jobs == null) {
43
+ action.payload[REDUCER_KEY].jobs = [];
44
+ }
45
+ return action.payload[REDUCER_KEY];
46
+ }
47
+ return state;
48
+ default:
49
+ return state;
50
+ }
51
+ };
@@ -0,0 +1,107 @@
1
+ import React, { Component } from 'react';
2
+ import _ from 'lodash';
3
+ import { TouchableOpacity, View, ScrollView, Text } from 'react-native';
4
+ import { connect } from 'react-redux';
5
+ import { Icon } from 'react-native-elements';
6
+ import { Services } from '../feature.config';
7
+ import { Components, Colours } from '../core.config';
8
+
9
+ class JobTypePicker extends Component {
10
+ state = {
11
+ currentType: null,
12
+ };
13
+
14
+ UNSAFE_componentWillMount() {
15
+ this.setState({ currentType: this.props.currentType });
16
+ }
17
+
18
+ onPressBack() {
19
+ Services.navigation.goBack();
20
+ }
21
+
22
+ onTypePress(type) {
23
+ this.props.onSelectType(type);
24
+ this.setState({ currentType: type });
25
+ setTimeout(() => {
26
+ this.onPressBack();
27
+ }, 200);
28
+ }
29
+
30
+ renderMain() {
31
+ if (_.isEmpty(this.props.types)) {
32
+ return (
33
+ <View style={{ marginTop: 16 }}>
34
+ <Components.Spinner />
35
+ </View>
36
+ );
37
+ }
38
+
39
+ return <Components.FormCard style={{ marginTop: 16 }}>{this.renderOptions()}</Components.FormCard>;
40
+ }
41
+
42
+ renderOptions() {
43
+ return this.props.types.map((rep, index) => {
44
+ return (
45
+ <TouchableOpacity key={index} onPress={this.onTypePress.bind(this, rep.typeName)}>
46
+ <Components.FormCardSection label={rep.typeName} labelStyle={{ height: 0, margin: 0 }} hasUnderline hasContent>
47
+ <View style={styles.labelContainer}>
48
+ <Text style={styles.labelText}>{rep.typeName}</Text>
49
+ <Icon
50
+ name="check-circle"
51
+ //style={styles.uploadButtonInner}
52
+ type="font-awesome"
53
+ iconStyle={[{ color: '#d5d9e0', fontSize: 20 }, rep.typeName === this.state.currentType && { color: Colours.COLOUR_GREEN }]}
54
+ />
55
+ </View>
56
+ {!_.isEmpty(rep.description) && <Text style={styles.description}>{rep.description}</Text>}
57
+ </Components.FormCardSection>
58
+ </TouchableOpacity>
59
+ );
60
+ });
61
+ }
62
+
63
+ render() {
64
+ return (
65
+ <View style={styles.container}>
66
+ <Components.Header leftIcon="angle-left" onPressLeft={this.onPressBack.bind(this)} text="Select type" />
67
+ <ScrollView style={{ flex: 1 }}>{this.renderMain()}</ScrollView>
68
+ </View>
69
+ );
70
+ }
71
+ }
72
+
73
+ const styles = {
74
+ container: {
75
+ flex: 1,
76
+ position: 'relative',
77
+ backgroundColor: '#f0f0f5',
78
+ },
79
+ row: {
80
+ flexDirection: 'row',
81
+ alignItems: 'center',
82
+ minHeight: 22,
83
+ },
84
+ text: {
85
+ flex: 1,
86
+ fontFamily: 'sf-regular',
87
+ fontSize: 14,
88
+ color: Colours.TEXT_DARK,
89
+ },
90
+ labelContainer: {
91
+ flexDirection: 'row',
92
+ justifyContent: 'space-between',
93
+ },
94
+ labelText: {
95
+ fontFamily: 'sf-medium',
96
+ fontSize: 16,
97
+ color: Colours.TEXT_DARK,
98
+ },
99
+ description: {
100
+ marginTop: 5,
101
+ fontSize: 14,
102
+ fontFamily: 'sf-regular',
103
+ color: Colours.TEXT_DARK,
104
+ },
105
+ };
106
+
107
+ export default connect(null, {})(JobTypePicker);