@plusscommunities/pluss-maintenance-app 2.1.4-beta.0 → 2.2.1-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.
- package/dist/module/actions/JobActions.js +7 -1
- package/dist/module/actions/JobActions.js.map +1 -1
- package/dist/module/actions/types.js +1 -0
- package/dist/module/actions/types.js.map +1 -1
- package/dist/module/apis/generalActions.js +17 -3
- package/dist/module/apis/generalActions.js.map +1 -1
- package/dist/module/components/FilterPopupMenu.js +249 -0
- package/dist/module/components/FilterPopupMenu.js.map +1 -0
- package/dist/module/components/MaintenanceList.js +166 -65
- package/dist/module/components/MaintenanceList.js.map +1 -1
- package/dist/module/components/MaintenanceListItem.js +23 -5
- package/dist/module/components/MaintenanceListItem.js.map +1 -1
- package/dist/module/reducers/JobsReducer.js +14 -1
- package/dist/module/reducers/JobsReducer.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/JobActions.js +8 -1
- package/src/actions/types.js +1 -0
- package/src/apis/generalActions.js +10 -3
- package/src/components/FilterPopupMenu.js +224 -0
- package/src/components/MaintenanceList.js +129 -54
- package/src/components/MaintenanceListItem.js +17 -3
- package/src/reducers/JobsReducer.js +8 -1
package/src/actions/types.js
CHANGED
@@ -4,18 +4,25 @@
|
|
4
4
|
import { Helper, Session } from '../core.config';
|
5
5
|
|
6
6
|
export const generalActions = {
|
7
|
-
getJob: (site,
|
7
|
+
getJob: (site, id) => {
|
8
|
+
return Session.authedFunction({
|
9
|
+
method: 'POST',
|
10
|
+
url: Helper.getUrl('maintenance', 'getJob'),
|
11
|
+
data: { site, id },
|
12
|
+
});
|
13
|
+
},
|
14
|
+
getJobByJobId: (site, jobId) => {
|
8
15
|
return Session.authedFunction({
|
9
16
|
method: 'POST',
|
10
17
|
url: Helper.getUrl('maintenance', 'getJob'),
|
11
18
|
data: { site, jobId },
|
12
19
|
});
|
13
20
|
},
|
14
|
-
getJobs: site => {
|
21
|
+
getJobs: (site, status = '', type = '') => {
|
15
22
|
return Session.authedFunction({
|
16
23
|
method: 'POST',
|
17
24
|
url: Helper.getUrl('maintenance', 'getJobs'),
|
18
|
-
data: { site },
|
25
|
+
data: { site, status, type },
|
19
26
|
});
|
20
27
|
},
|
21
28
|
sendMaintenanceRequest: (userID, userName, phone, room, title, description, date, type, images, location, isHome, homeText) => {
|
@@ -0,0 +1,224 @@
|
|
1
|
+
import React, { Component } from 'react';
|
2
|
+
import { View, Text, TouchableOpacity, Modal } from 'react-native';
|
3
|
+
import { connect } from 'react-redux';
|
4
|
+
import _ from 'lodash';
|
5
|
+
import { generalActions } from '../apis';
|
6
|
+
import { Colours, Helper } from '../core.config';
|
7
|
+
|
8
|
+
class FilterPopupMenu extends Component {
|
9
|
+
constructor(props) {
|
10
|
+
super(props);
|
11
|
+
|
12
|
+
this.state = {
|
13
|
+
types: props.types || [],
|
14
|
+
selectedStatus: props.status || '',
|
15
|
+
selectedType: props.type || '',
|
16
|
+
};
|
17
|
+
this.statusOptions = [
|
18
|
+
{
|
19
|
+
label: 'All',
|
20
|
+
value: '',
|
21
|
+
},
|
22
|
+
{
|
23
|
+
label: 'Incomplete',
|
24
|
+
value: 'Unassigned|In Progress',
|
25
|
+
},
|
26
|
+
{
|
27
|
+
label: 'Unassigned',
|
28
|
+
value: 'Unassigned',
|
29
|
+
},
|
30
|
+
{
|
31
|
+
label: 'In Progress',
|
32
|
+
value: 'In Progress',
|
33
|
+
},
|
34
|
+
{
|
35
|
+
label: 'Completed',
|
36
|
+
value: 'Completed',
|
37
|
+
},
|
38
|
+
];
|
39
|
+
}
|
40
|
+
|
41
|
+
componentDidMount() {
|
42
|
+
if (_.isEmpty(this.state.types)) this.refreshTypes();
|
43
|
+
}
|
44
|
+
|
45
|
+
componentDidUpdate(prevProps) {
|
46
|
+
if (prevProps.site !== this.props.site) {
|
47
|
+
this.refreshTypes();
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
refreshTypes = async () => {
|
52
|
+
const { data } = await generalActions.getJobTypes(Helper.getSite(this.props.site));
|
53
|
+
const types = data.map(t => {
|
54
|
+
return { label: t.typeName, value: t.typeName };
|
55
|
+
});
|
56
|
+
types.splice(0, 0, { label: 'All', value: '' });
|
57
|
+
// console.log('refreshTypes', types);
|
58
|
+
this.setState({ types });
|
59
|
+
};
|
60
|
+
|
61
|
+
onSelectOption = (key, value) => {
|
62
|
+
const newState = {};
|
63
|
+
newState[key] = value;
|
64
|
+
this.setState(newState);
|
65
|
+
};
|
66
|
+
|
67
|
+
onDone = () => {
|
68
|
+
const { onClose } = this.props;
|
69
|
+
const { selectedStatus, selectedType } = this.state;
|
70
|
+
if (onClose)
|
71
|
+
onClose({
|
72
|
+
status: selectedStatus,
|
73
|
+
type: selectedType,
|
74
|
+
});
|
75
|
+
};
|
76
|
+
|
77
|
+
renderTitle() {
|
78
|
+
const { title } = this.props;
|
79
|
+
return (
|
80
|
+
<View style={styles.titleContainer}>
|
81
|
+
<Text style={styles.titleText}>{title || 'Filter By'}</Text>
|
82
|
+
</View>
|
83
|
+
);
|
84
|
+
}
|
85
|
+
|
86
|
+
renderCancel() {
|
87
|
+
const { colourBrandingMain, cancelText } = this.props;
|
88
|
+
return (
|
89
|
+
<TouchableOpacity onPress={this.onDone}>
|
90
|
+
<View style={styles.cancelContainer}>
|
91
|
+
<Text style={[styles.cancelText, { color: colourBrandingMain }]}>{cancelText || 'Done'}</Text>
|
92
|
+
</View>
|
93
|
+
</TouchableOpacity>
|
94
|
+
);
|
95
|
+
}
|
96
|
+
|
97
|
+
renderOptions(title, options, selectedKey) {
|
98
|
+
const { colourBrandingMain } = this.props;
|
99
|
+
return (
|
100
|
+
<View style={styles.optionsContainer}>
|
101
|
+
<Text style={styles.optionsTitle}>{title}</Text>
|
102
|
+
<View style={styles.options}>
|
103
|
+
{options.map(o => {
|
104
|
+
const selected = this.state[selectedKey];
|
105
|
+
const backgroundColor = o.value === selected ? colourBrandingMain : '#fff';
|
106
|
+
const color = o.value === selected ? '#fff' : colourBrandingMain;
|
107
|
+
return (
|
108
|
+
<TouchableOpacity key={o.label} onPress={() => this.onSelectOption(selectedKey, o.value)}>
|
109
|
+
<View style={[styles.optionContainer, { backgroundColor, borderColor: colourBrandingMain }]}>
|
110
|
+
<Text style={[styles.optionText, { color }]}>{o.label}</Text>
|
111
|
+
</View>
|
112
|
+
</TouchableOpacity>
|
113
|
+
);
|
114
|
+
})}
|
115
|
+
</View>
|
116
|
+
</View>
|
117
|
+
);
|
118
|
+
}
|
119
|
+
|
120
|
+
render() {
|
121
|
+
return (
|
122
|
+
<Modal visible transparent animationType="slide" onRequestClose={this.onDone}>
|
123
|
+
<View style={styles.container}>
|
124
|
+
<View style={styles.menu}>
|
125
|
+
{this.renderTitle()}
|
126
|
+
<View style={styles.optionContent}>
|
127
|
+
{this.renderOptions('Status', this.statusOptions, 'selectedStatus')}
|
128
|
+
{this.renderOptions('Type', this.state.types, 'selectedType')}
|
129
|
+
</View>
|
130
|
+
{this.renderCancel()}
|
131
|
+
</View>
|
132
|
+
</View>
|
133
|
+
</Modal>
|
134
|
+
);
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
const styles = {
|
139
|
+
container: {
|
140
|
+
position: 'absolute',
|
141
|
+
bottom: 0,
|
142
|
+
left: 0,
|
143
|
+
right: 0,
|
144
|
+
top: 0,
|
145
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
146
|
+
zIndex: 1000,
|
147
|
+
},
|
148
|
+
menu: {
|
149
|
+
position: 'absolute',
|
150
|
+
bottom: 0,
|
151
|
+
left: 0,
|
152
|
+
right: 0,
|
153
|
+
backgroundColor: '#fff',
|
154
|
+
borderTopLeftRadius: 12,
|
155
|
+
borderTopRightRadius: 12,
|
156
|
+
},
|
157
|
+
cancelContainer: {
|
158
|
+
paddingVertical: 16,
|
159
|
+
borderColor: Colours.LINEGREY,
|
160
|
+
borderTopWidth: 1,
|
161
|
+
paddingHorizontal: 16,
|
162
|
+
},
|
163
|
+
cancelText: {
|
164
|
+
fontFamily: 'sf-medium',
|
165
|
+
fontSize: 16,
|
166
|
+
textAlign: 'center',
|
167
|
+
color: Colours.TEXT_DARK,
|
168
|
+
},
|
169
|
+
titleContainer: {
|
170
|
+
padding: 16,
|
171
|
+
borderColor: Colours.LINEGREY,
|
172
|
+
borderBottomWidth: 1,
|
173
|
+
},
|
174
|
+
titleText: {
|
175
|
+
fontFamily: 'sf-semibold',
|
176
|
+
fontSize: 16,
|
177
|
+
textAlign: 'center',
|
178
|
+
color: Colours.TEXT_DARK,
|
179
|
+
},
|
180
|
+
optionContent: {
|
181
|
+
paddingVertical: 10,
|
182
|
+
},
|
183
|
+
optionsContainer: {
|
184
|
+
marginHorizontal: 14,
|
185
|
+
},
|
186
|
+
optionsTitle: {
|
187
|
+
fontFamily: 'sf-semibold',
|
188
|
+
marginBottom: 4,
|
189
|
+
},
|
190
|
+
options: {
|
191
|
+
flex: 1,
|
192
|
+
flexDirection: 'row',
|
193
|
+
flexWrap: 'wrap',
|
194
|
+
},
|
195
|
+
optionContainer: {
|
196
|
+
flexDirection: 'row',
|
197
|
+
alignItems: 'center',
|
198
|
+
justifyContent: 'center',
|
199
|
+
minWidth: 50,
|
200
|
+
height: 30,
|
201
|
+
paddingHorizontal: 8,
|
202
|
+
borderRadius: 4,
|
203
|
+
borderWidth: 1,
|
204
|
+
marginRight: 8,
|
205
|
+
marginBottom: 8,
|
206
|
+
},
|
207
|
+
optionText: {
|
208
|
+
color: '#fff',
|
209
|
+
textAlign: 'center',
|
210
|
+
fontFamily: 'sf-semibold',
|
211
|
+
fontSize: 13,
|
212
|
+
},
|
213
|
+
};
|
214
|
+
|
215
|
+
const mapStateToProps = state => {
|
216
|
+
const { user } = state;
|
217
|
+
|
218
|
+
return {
|
219
|
+
site: user.site,
|
220
|
+
colourBrandingMain: Colours.getMainBrandingColourFromState(state),
|
221
|
+
};
|
222
|
+
};
|
223
|
+
|
224
|
+
export default connect(mapStateToProps, {})(FilterPopupMenu);
|
@@ -1,44 +1,52 @@
|
|
1
1
|
import React, { Component } from 'react';
|
2
2
|
import { View, StyleSheet, FlatList, TouchableOpacity, Text } from 'react-native';
|
3
|
-
import { Icon } from 'react-native-elements';
|
4
3
|
import _ from 'lodash';
|
5
4
|
import { connect } from 'react-redux';
|
6
5
|
import { generalActions } from '../apis';
|
7
|
-
import { jobsLoaded } from '../actions';
|
6
|
+
import { jobsLoaded, jobAdded, jobsAdded } from '../actions';
|
8
7
|
import MaintenanceListItem from '../components/MaintenanceListItem';
|
9
|
-
import
|
10
|
-
import { Components, Colours, Config } from '../core.config';
|
11
|
-
|
12
|
-
const SHOW_ALL_STATUS = 'Show All';
|
8
|
+
import FilterPopupMenu from './FilterPopupMenu';
|
9
|
+
import { Components, Colours, Config, Helper } from '../core.config';
|
13
10
|
|
14
11
|
class MaintenanceList extends Component {
|
15
12
|
constructor(props) {
|
16
13
|
super(props);
|
17
14
|
|
18
15
|
this.state = {
|
16
|
+
types: [],
|
17
|
+
filteredList: props.jobs,
|
19
18
|
loading: false,
|
20
|
-
|
21
|
-
|
19
|
+
searchText: '',
|
20
|
+
showFilterPopup: false,
|
21
|
+
selectedStatus: props.hasPermission ? 'Unassigned|In Progress' : '',
|
22
|
+
selectedType: '',
|
22
23
|
};
|
23
24
|
}
|
24
25
|
|
25
26
|
componentDidMount() {
|
26
27
|
this.refresh();
|
28
|
+
this.refreshTypes();
|
29
|
+
|
30
|
+
this.resetDataSource();
|
27
31
|
}
|
28
32
|
|
29
33
|
componentDidUpdate(prevProps) {
|
30
|
-
if (!_.isEqual(prevProps.statuses, this.props.statuses) && !this.props.statuses.includes(this.state.selectedStatus)) {
|
31
|
-
// Reset selected status if not exists
|
32
|
-
this.setState({ selectedStatus: SHOW_ALL_STATUS });
|
33
|
-
}
|
34
34
|
if (!prevProps.dataUpdated && this.props.dataUpdated) this.refresh();
|
35
|
+
if (!_.isEqual(prevProps.jobs, this.props.jobs)) this.resetDataSource();
|
35
36
|
}
|
36
37
|
|
37
38
|
refresh = () => {
|
38
39
|
this.onLoadingChanged(true, async () => {
|
39
40
|
try {
|
40
|
-
const
|
41
|
-
|
41
|
+
const { selectedStatus, selectedType } = this.state;
|
42
|
+
// console.log('filters', { selectedStatus, selectedType });
|
43
|
+
const res = await generalActions.getJobs(this.props.site, selectedStatus, selectedType);
|
44
|
+
// console.log('refresh', res?.data);
|
45
|
+
if (selectedStatus || selectedType) {
|
46
|
+
this.props.jobsAdded(res.data);
|
47
|
+
} else {
|
48
|
+
this.props.jobsLoaded(res.data);
|
49
|
+
}
|
42
50
|
} catch (error) {
|
43
51
|
console.log('refresh error', error);
|
44
52
|
} finally {
|
@@ -47,6 +55,32 @@ class MaintenanceList extends Component {
|
|
47
55
|
});
|
48
56
|
};
|
49
57
|
|
58
|
+
refreshTypes = async () => {
|
59
|
+
const { data } = await generalActions.getJobTypes(Helper.getSite(this.props.site));
|
60
|
+
const types = data.map(t => {
|
61
|
+
return { label: t.typeName, value: t.typeName };
|
62
|
+
});
|
63
|
+
types.splice(0, 0, { label: 'All', value: '' });
|
64
|
+
// console.log('refreshTypes', types);
|
65
|
+
this.setState({ types });
|
66
|
+
};
|
67
|
+
|
68
|
+
fetchJob = jobId => {
|
69
|
+
if (this.state.loading) return;
|
70
|
+
|
71
|
+
this.onLoadingChanged(true, async () => {
|
72
|
+
try {
|
73
|
+
const job = await generalActions.getJobByJobId(this.props.site, jobId);
|
74
|
+
// console.log('fetchJob', job?.data);
|
75
|
+
this.props.jobAdded(job.data);
|
76
|
+
} catch (error) {
|
77
|
+
console.log('fetchJob error', error);
|
78
|
+
} finally {
|
79
|
+
this.onLoadingChanged(false);
|
80
|
+
}
|
81
|
+
});
|
82
|
+
};
|
83
|
+
|
50
84
|
getEmptyStateText() {
|
51
85
|
if (this.props.options && !_.isEmpty(this.props.options.EmptyText)) {
|
52
86
|
return this.props.options.EmptyText;
|
@@ -54,6 +88,23 @@ class MaintenanceList extends Component {
|
|
54
88
|
return this.props.userCategory === 'staff' ? Config.env.strings.EMPTY_REQUESTS_STAFF : Config.env.strings.EMPTY_REQUESTS_USER;
|
55
89
|
}
|
56
90
|
|
91
|
+
resetDataSource = (source = '') => {
|
92
|
+
const { searchText, selectedStatus, selectedType } = this.state;
|
93
|
+
const { jobs } = this.props;
|
94
|
+
|
95
|
+
let filteredList = jobs;
|
96
|
+
if (searchText) {
|
97
|
+
filteredList = jobs.filter(j => j.jobId === searchText);
|
98
|
+
if (filteredList.length === 0) this.fetchJob(searchText);
|
99
|
+
} else if (source !== 'search') {
|
100
|
+
if (selectedStatus) filteredList = filteredList.filter(j => selectedStatus.includes(j.status));
|
101
|
+
if (selectedType) filteredList = filteredList.filter(j => selectedType.includes(j.type));
|
102
|
+
this.refresh();
|
103
|
+
}
|
104
|
+
|
105
|
+
this.setState({ filteredList });
|
106
|
+
};
|
107
|
+
|
57
108
|
onLoadingChanged = (loading, callback) => {
|
58
109
|
this.setState({ loading }, () => {
|
59
110
|
if (this.props.onLoadingChanged) this.props.onLoadingChanged(this.state.loading);
|
@@ -61,36 +112,56 @@ class MaintenanceList extends Component {
|
|
61
112
|
});
|
62
113
|
};
|
63
114
|
|
64
|
-
|
65
|
-
this.setState({
|
115
|
+
onSearchText = value => {
|
116
|
+
this.setState({ searchText: value }, () => {
|
117
|
+
if (_.isEmpty(this.state.searchText)) this.resetDataSource('search');
|
118
|
+
});
|
66
119
|
};
|
67
120
|
|
68
|
-
|
69
|
-
this.
|
121
|
+
onSearchSubmit = () => {
|
122
|
+
this.resetDataSource('search');
|
70
123
|
};
|
71
124
|
|
72
|
-
|
73
|
-
this.setState({
|
125
|
+
onToggleFilter = () => {
|
126
|
+
this.setState({ showFilterPopup: !this.state.showFilterPopup });
|
127
|
+
};
|
128
|
+
|
129
|
+
onSelectFilter = selected => {
|
130
|
+
this.setState({ selectedStatus: selected.status, selectedType: selected.type }, () => {
|
131
|
+
this.resetDataSource();
|
132
|
+
this.onToggleFilter();
|
133
|
+
});
|
74
134
|
};
|
75
135
|
|
76
136
|
renderEmptyList() {
|
77
137
|
return this.state.loading ? null : <Components.EmptyStateMain title={this.getEmptyStateText()} style={{ marginHorizontal: 16 }} />;
|
78
138
|
}
|
79
139
|
|
80
|
-
|
81
|
-
|
140
|
+
renderFilterButton() {
|
141
|
+
return (
|
142
|
+
<TouchableOpacity onPress={this.onToggleFilter}>
|
143
|
+
<View style={styles.filterButton}>
|
144
|
+
<Text style={[styles.filterButtonText, { color: this.props.colourBrandingMain }]}>Filter</Text>
|
145
|
+
</View>
|
146
|
+
</TouchableOpacity>
|
147
|
+
);
|
148
|
+
}
|
149
|
+
|
150
|
+
renderSearch() {
|
151
|
+
if (!this.props.hasPermission) return null;
|
82
152
|
|
83
153
|
return (
|
84
|
-
<View style={styles.
|
85
|
-
<
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
154
|
+
<View style={styles.searchContainer}>
|
155
|
+
<Components.GenericInput
|
156
|
+
placeholder="Search by Job ID"
|
157
|
+
value={this.state.searchText}
|
158
|
+
onChangeText={this.onSearchText}
|
159
|
+
onSubmitEditing={this.onSearchSubmit}
|
160
|
+
squaredCorners
|
161
|
+
hasClear
|
162
|
+
keyboardType={'numeric'}
|
163
|
+
returnKeyType={'done'}
|
164
|
+
/>
|
94
165
|
</View>
|
95
166
|
);
|
96
167
|
}
|
@@ -99,15 +170,14 @@ class MaintenanceList extends Component {
|
|
99
170
|
return (
|
100
171
|
<View>
|
101
172
|
{this.props.ListHeaderComponent}
|
102
|
-
{this.
|
173
|
+
{this.renderFilterButton()}
|
174
|
+
{this.renderSearch()}
|
103
175
|
</View>
|
104
176
|
);
|
105
177
|
}
|
106
178
|
|
107
179
|
renderList() {
|
108
|
-
const {
|
109
|
-
const { jobs } = this.props;
|
110
|
-
const filteredList = selectedStatus === SHOW_ALL_STATUS ? jobs : jobs.filter(job => job.status === selectedStatus);
|
180
|
+
const { filteredList } = this.state;
|
111
181
|
|
112
182
|
return (
|
113
183
|
<FlatList
|
@@ -123,21 +193,12 @@ class MaintenanceList extends Component {
|
|
123
193
|
);
|
124
194
|
}
|
125
195
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
renderStatusPopup() {
|
131
|
-
if (!this.state.showStatusPopup) return null;
|
196
|
+
renderFilterPopup() {
|
197
|
+
const { showFilterPopup, types, selectedStatus, selectedType } = this.state;
|
198
|
+
if (!showFilterPopup) return null;
|
132
199
|
|
133
200
|
return (
|
134
|
-
<
|
135
|
-
filter={this.props.statuses}
|
136
|
-
includeAll
|
137
|
-
allText={SHOW_ALL_STATUS}
|
138
|
-
onClose={this.onCloseFilter}
|
139
|
-
onSelect={this.onSelectStatus}
|
140
|
-
/>
|
201
|
+
<FilterPopupMenu site={this.props.site} types={types} status={selectedStatus} type={selectedType} onClose={this.onSelectFilter} />
|
141
202
|
);
|
142
203
|
}
|
143
204
|
|
@@ -145,8 +206,7 @@ class MaintenanceList extends Component {
|
|
145
206
|
return (
|
146
207
|
<View style={[styles.container, this.props.style]}>
|
147
208
|
{this.renderList()}
|
148
|
-
{
|
149
|
-
{/* {this.renderStatusPopup()} */}
|
209
|
+
{this.renderFilterPopup()}
|
150
210
|
</View>
|
151
211
|
);
|
152
212
|
}
|
@@ -182,16 +242,31 @@ const styles = StyleSheet.create({
|
|
182
242
|
filterIcon: {
|
183
243
|
fontSize: 20,
|
184
244
|
},
|
245
|
+
searchContainer: {
|
246
|
+
flexDirection: 'row',
|
247
|
+
alignItems: 'center',
|
248
|
+
paddingBottom: 8,
|
249
|
+
paddingHorizontal: 16,
|
250
|
+
},
|
251
|
+
filterButton: {
|
252
|
+
position: 'absolute',
|
253
|
+
right: 20,
|
254
|
+
top: -32,
|
255
|
+
},
|
256
|
+
filterButtonText: {
|
257
|
+
fontFamily: 'sf-semibold',
|
258
|
+
fontSize: 16,
|
259
|
+
},
|
185
260
|
});
|
186
261
|
|
187
262
|
const mapStateToProps = state => {
|
188
263
|
const { user, jobs, notifications } = state;
|
189
264
|
const jobsOrdered = _.orderBy(jobs.jobs, ['createdUnix'], ['desc']);
|
190
|
-
const
|
265
|
+
const hasPermission = _.includes(user.permissions, 'maintenanceTracking');
|
191
266
|
|
192
267
|
return {
|
268
|
+
hasPermission,
|
193
269
|
jobs: jobsOrdered,
|
194
|
-
statuses,
|
195
270
|
site: user.site,
|
196
271
|
userCategory: user.category,
|
197
272
|
colourBrandingMain: Colours.getMainBrandingColourFromState(state),
|
@@ -199,4 +274,4 @@ const mapStateToProps = state => {
|
|
199
274
|
};
|
200
275
|
};
|
201
276
|
|
202
|
-
export default connect(mapStateToProps, { jobsLoaded }, null, { forwardRef: true })(MaintenanceList);
|
277
|
+
export default connect(mapStateToProps, { jobsLoaded, jobAdded, jobsAdded }, null, { forwardRef: true })(MaintenanceList);
|
@@ -72,7 +72,9 @@ class MaintenanceListItem extends Component {
|
|
72
72
|
<View style={styles.jobInnerContainer}>
|
73
73
|
<View style={styles.jobTopSection}>
|
74
74
|
<View style={styles.jobTopLeft}>
|
75
|
+
{job.jobId ? <Text style={[styles.jobIdText, { color: this.props.colourBrandingMain }]}>{`Job #${job.jobId}`}</Text> : null}
|
75
76
|
<Text style={styles.jobTitleText}>{job.title}</Text>
|
77
|
+
{job.room ? <Text style={styles.jobLocationText}>{job.room}</Text> : null}
|
76
78
|
<View style={styles.jobTypeSeenContainer}>
|
77
79
|
<View style={[styles.jobTypeContainer, { backgroundColor: Colours.hexToRGBAstring(this.props.colourBrandingMain, 0.2) }]}>
|
78
80
|
<Text style={[styles.jobTypeText, { color: this.props.colourBrandingMain }]}>{job.type}</Text>
|
@@ -117,22 +119,34 @@ const styles = StyleSheet.create({
|
|
117
119
|
},
|
118
120
|
jobTopSection: {
|
119
121
|
flexDirection: 'row',
|
120
|
-
alignItems: '
|
122
|
+
alignItems: 'flex-end',
|
121
123
|
paddingHorizontal: 10,
|
122
|
-
paddingVertical:
|
124
|
+
paddingVertical: 10,
|
123
125
|
borderBottomWidth: 1,
|
124
126
|
borderBottomColor: Colours.LINEGREY,
|
125
127
|
},
|
126
128
|
jobTopLeft: {
|
127
129
|
flex: 1,
|
128
130
|
},
|
131
|
+
jobIdText: {
|
132
|
+
fontFamily: 'sf-medium',
|
133
|
+
fontSize: 12,
|
134
|
+
marginBottom: 4,
|
135
|
+
},
|
129
136
|
jobTitleText: {
|
130
137
|
fontFamily: 'sf-semibold',
|
131
138
|
fontSize: 18,
|
132
139
|
color: Colours.TEXT_DARK,
|
133
|
-
marginBottom:
|
140
|
+
marginBottom: 4,
|
141
|
+
},
|
142
|
+
jobLocationText: {
|
143
|
+
fontFamily: 'sf-medium',
|
144
|
+
fontSize: 12,
|
145
|
+
color: Colours.TEXT_LIGHT,
|
146
|
+
marginBottom: 4,
|
134
147
|
},
|
135
148
|
jobTypeSeenContainer: {
|
149
|
+
marginTop: 4,
|
136
150
|
flexDirection: 'row',
|
137
151
|
alignItems: 'center',
|
138
152
|
},
|
@@ -1,6 +1,7 @@
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
2
|
+
import _ from 'lodash';
|
2
3
|
import { REHYDRATE } from 'redux-persist';
|
3
|
-
import { JOBS_LOADED, JOB_ADDED } from '../actions/types';
|
4
|
+
import { JOBS_LOADED, JOB_ADDED, JOBS_ADDED } from '../actions/types';
|
4
5
|
import { ActionTypes } from '../core.config';
|
5
6
|
|
6
7
|
const REDUCER_KEY = 'jobs';
|
@@ -19,6 +20,12 @@ export default (state = INITIAL_STATE, action) => {
|
|
19
20
|
return INITIAL_STATE;
|
20
21
|
case JOBS_LOADED:
|
21
22
|
return { ...state, jobs: action.payload.map(job => ({ title: job.title || job.description, ...job })) };
|
23
|
+
case JOBS_ADDED:
|
24
|
+
updateJobs = action.payload.map(job => ({ title: job.title || job.description, ...job }));
|
25
|
+
updateJobs = _.unionWith(updateJobs, state.jobs, (j1, j2) => {
|
26
|
+
return j1.id === j2.id;
|
27
|
+
});
|
28
|
+
return { ...state, jobs: updateJobs };
|
22
29
|
case JOB_ADDED:
|
23
30
|
updateJobs = [...state.jobs];
|
24
31
|
index = updateJobs.findIndex(item => item.id === action.payload.id);
|