@plusscommunities/pluss-maintenance-web 1.2.4-beta.0 → 1.2.4-beta.2
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/{index.esm.js → index.js} +3176 -2644
- package/dist/index.js.map +1 -0
- package/package.json +13 -20
- package/.babelrc +0 -3
- package/dist/index.cjs.js +0 -6234
- package/dist/index.umd.js +0 -6227
- package/rollup.config.js +0 -59
- package/src/actions/JobsActions.js +0 -150
- package/src/actions/index.js +0 -1
- package/src/actions/types.js +0 -8
- package/src/apis/index.js +0 -10
- package/src/apis/maintenanceActions.js +0 -203
- package/src/apis/reactionActions.js +0 -46
- package/src/components/ActivityText.js +0 -57
- package/src/components/AnalyticsHub.js +0 -167
- package/src/components/Configuration.js +0 -392
- package/src/components/JobList.js +0 -1108
- package/src/components/JobTypes.js +0 -198
- package/src/components/PreviewFull.js +0 -33
- package/src/components/PreviewGrid.js +0 -29
- package/src/components/PreviewWidget.js +0 -35
- package/src/components/ViewFull.js +0 -25
- package/src/components/ViewWidget.js +0 -23
- package/src/feature.config.js +0 -127
- package/src/helper/index.js +0 -26
- package/src/images/forms/full.png +0 -0
- package/src/images/forms/fullNoTitle.png +0 -0
- package/src/images/forms/previewWidget.png +0 -0
- package/src/images/forms/widget.png +0 -0
- package/src/images/full.png +0 -0
- package/src/images/fullNoTitle.png +0 -0
- package/src/images/previewWidget.png +0 -0
- package/src/images/widget.png +0 -0
- package/src/index.js +0 -29
- package/src/maintenancePriority.json +0 -5
- package/src/maintenanceStatus.json +0 -20
- package/src/reducers/MaintenanceReducer.js +0 -49
- package/src/screens/AddJob.js +0 -1138
- package/src/screens/AddJobType.js +0 -865
- package/src/screens/Job.js +0 -1531
- package/src/screens/RequestsHub.js +0 -237
- package/src/values.config.a.js +0 -63
- package/src/values.config.default.js +0 -75
- package/src/values.config.enquiry.js +0 -76
- package/src/values.config.feedback.js +0 -74
- package/src/values.config.food.js +0 -74
- package/src/values.config.forms.js +0 -74
- package/src/values.config.js +0 -75
package/src/screens/Job.js
DELETED
|
@@ -1,1531 +0,0 @@
|
|
|
1
|
-
import React, { Component } from "react";
|
|
2
|
-
import { withRouter } from "react-router";
|
|
3
|
-
import { Link } from "react-router-dom";
|
|
4
|
-
import moment from "moment";
|
|
5
|
-
import _ from "lodash";
|
|
6
|
-
import FontAwesome from "react-fontawesome";
|
|
7
|
-
import Textarea from "react-textarea-autosize";
|
|
8
|
-
import { connect } from "react-redux";
|
|
9
|
-
import { jobsLoaded, jobStatusesUpdate } from "../actions";
|
|
10
|
-
import Config, { PlussCore } from "../feature.config";
|
|
11
|
-
import { maintenanceActions, reactionActions } from "../apis";
|
|
12
|
-
import {
|
|
13
|
-
STATUS_NOT_ACTIONED,
|
|
14
|
-
jobPriorityOptions,
|
|
15
|
-
getJobPriority,
|
|
16
|
-
} from "../helper";
|
|
17
|
-
import { values } from "../values.config";
|
|
18
|
-
|
|
19
|
-
const { Apis, Helper, Session, Colours, Components } = PlussCore;
|
|
20
|
-
const IMAGE_SIZE_NOTE = 72;
|
|
21
|
-
|
|
22
|
-
class Job extends Component {
|
|
23
|
-
constructor(props) {
|
|
24
|
-
super(props);
|
|
25
|
-
this.state = {
|
|
26
|
-
jobId: Helper.safeReadParams(props, "jobId")
|
|
27
|
-
? props.match.params.jobId
|
|
28
|
-
: null,
|
|
29
|
-
job: null,
|
|
30
|
-
showingSelector: false,
|
|
31
|
-
updating: false,
|
|
32
|
-
comments: [],
|
|
33
|
-
commentInput: "",
|
|
34
|
-
loadingComments: false,
|
|
35
|
-
priorityChangerOpen: false,
|
|
36
|
-
statusChangerOpen: false,
|
|
37
|
-
addNoteOpen: false,
|
|
38
|
-
noteAttachments: [],
|
|
39
|
-
noteInput: "",
|
|
40
|
-
noteImages: [],
|
|
41
|
-
assignees: [],
|
|
42
|
-
externalSync: null,
|
|
43
|
-
loadingExternalSync: false,
|
|
44
|
-
retryingSync: false,
|
|
45
|
-
retrySyncError: null,
|
|
46
|
-
retrySyncInitiated: false,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
51
|
-
Session.checkLoggedIn(this, this.props.auth);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
componentDidMount() {
|
|
55
|
-
this.props.jobStatusesUpdate(this.props.auth.site);
|
|
56
|
-
if (this.state.jobId) {
|
|
57
|
-
this.getJob();
|
|
58
|
-
this.getComments();
|
|
59
|
-
this.getAssignees();
|
|
60
|
-
this.getExternalSync();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
getJob = async () => {
|
|
65
|
-
try {
|
|
66
|
-
const res = await maintenanceActions.getJob(
|
|
67
|
-
this.props.auth.site,
|
|
68
|
-
this.state.jobId,
|
|
69
|
-
);
|
|
70
|
-
this.setState({ updating: false });
|
|
71
|
-
res.data.location = res.data.site;
|
|
72
|
-
this.setJob(res.data);
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error("getJob", error);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
getAssignees = async () => {
|
|
79
|
-
try {
|
|
80
|
-
const res = await maintenanceActions.getAssignees(this.props.auth.site);
|
|
81
|
-
this.setState({ assignees: res.data.Users });
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error("getAssignees", error);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
getExternalSync = async () => {
|
|
88
|
-
try {
|
|
89
|
-
this.setState({ loadingExternalSync: true });
|
|
90
|
-
const res = await maintenanceActions.getExternalSync(this.state.jobId);
|
|
91
|
-
this.setState({ externalSync: res.data, loadingExternalSync: false });
|
|
92
|
-
} catch (error) {
|
|
93
|
-
// 404 is expected if no sync - don't show error
|
|
94
|
-
if (error && error.response && error.response.status !== 404) {
|
|
95
|
-
console.error("getExternalSync", error);
|
|
96
|
-
}
|
|
97
|
-
this.setState({ loadingExternalSync: false });
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
onRetrySync = async () => {
|
|
102
|
-
const { job } = this.state;
|
|
103
|
-
if (!job || this.state.retryingSync) return;
|
|
104
|
-
|
|
105
|
-
this.setState({ retryingSync: true, retrySyncError: null });
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
await maintenanceActions.retrySync(job.id);
|
|
109
|
-
// Refresh job data to get updated history
|
|
110
|
-
await this.getJob();
|
|
111
|
-
this.setState({ retryingSync: false, retrySyncInitiated: true });
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error("onRetrySync", error);
|
|
114
|
-
const errorMessage =
|
|
115
|
-
(error &&
|
|
116
|
-
error.response &&
|
|
117
|
-
error.response.data &&
|
|
118
|
-
error.response.data.error) ||
|
|
119
|
-
"Failed to retry sync. Please try again.";
|
|
120
|
-
this.setState({ retryingSync: false, retrySyncError: errorMessage });
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
getStatusType = (status) => {
|
|
125
|
-
const { statusTypes } = this.props;
|
|
126
|
-
let statusType = statusTypes.find((s) => s.text === status);
|
|
127
|
-
if (!statusType) {
|
|
128
|
-
const defaultStatus = statusTypes.find(
|
|
129
|
-
(s) => s.category === STATUS_NOT_ACTIONED,
|
|
130
|
-
);
|
|
131
|
-
statusType = { ...defaultStatus, text: status };
|
|
132
|
-
}
|
|
133
|
-
return statusType;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
setJob = (job) => {
|
|
137
|
-
if (_.isEmpty(job.lastActivity)) {
|
|
138
|
-
job.lastActivity = "-- --";
|
|
139
|
-
job.noActivity = true;
|
|
140
|
-
}
|
|
141
|
-
if (_.isEmpty(job.status)) {
|
|
142
|
-
job.status = "Unassigned";
|
|
143
|
-
job.notStatus = true;
|
|
144
|
-
}
|
|
145
|
-
if (_.isEmpty(job.audience)) {
|
|
146
|
-
job.audience = [{ displayName: "Unassigned", isEmpty: true }];
|
|
147
|
-
}
|
|
148
|
-
let needToMarkSeen = false;
|
|
149
|
-
if (!job.seen) {
|
|
150
|
-
job.seen = true;
|
|
151
|
-
needToMarkSeen = true;
|
|
152
|
-
}
|
|
153
|
-
this.setState({ job }, () => {
|
|
154
|
-
if (needToMarkSeen) this.markSeen();
|
|
155
|
-
});
|
|
156
|
-
this.props.jobsLoaded([job]);
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
editJob = () => {};
|
|
160
|
-
|
|
161
|
-
isReadyToSaveNote = () => {
|
|
162
|
-
const { noteAttachments, noteInput, noteImages } = this.state;
|
|
163
|
-
if (_.some(noteAttachments, (n) => n.Uploading)) return false;
|
|
164
|
-
return (
|
|
165
|
-
!_.isEmpty(noteInput) ||
|
|
166
|
-
!_.isEmpty(noteAttachments) ||
|
|
167
|
-
!_.isEmpty(noteImages)
|
|
168
|
-
);
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
onOpenAddNote = () => {
|
|
172
|
-
this.setState({ addNoteOpen: true, editingNote: null });
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
onCloseAddNote = () => {
|
|
176
|
-
const newState = { addNoteOpen: false, editingNote: null };
|
|
177
|
-
if (!!this.state.editingNote) {
|
|
178
|
-
newState.noteInput = "";
|
|
179
|
-
newState.noteAttachments = [];
|
|
180
|
-
newState.noteImages = [];
|
|
181
|
-
}
|
|
182
|
-
this.setState(newState);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
onOpenNoteMenu = (index) => {
|
|
186
|
-
if (this.state.noteMenuOpen === index) {
|
|
187
|
-
this.setState({ noteMenuOpen: null });
|
|
188
|
-
} else {
|
|
189
|
-
this.setState({ noteMenuOpen: index });
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
onHandlePDFFileChange = (event) => {
|
|
194
|
-
const file = event.target.files[0];
|
|
195
|
-
if (!file || this.state.uploadingNoteAttachment) return;
|
|
196
|
-
const noteAttachments = [...this.state.noteAttachments];
|
|
197
|
-
const newAttachment = {
|
|
198
|
-
Uploading: true,
|
|
199
|
-
Title: file.name,
|
|
200
|
-
};
|
|
201
|
-
noteAttachments.push(newAttachment);
|
|
202
|
-
this.setState({
|
|
203
|
-
noteAttachments,
|
|
204
|
-
});
|
|
205
|
-
Apis.fileActions
|
|
206
|
-
.uploadMediaAsync(file, file.name)
|
|
207
|
-
.then((fileRes) => {
|
|
208
|
-
newAttachment.Source = fileRes;
|
|
209
|
-
newAttachment.Uploading = false;
|
|
210
|
-
this.setState({
|
|
211
|
-
noteAttachments: [...this.state.noteAttachments],
|
|
212
|
-
});
|
|
213
|
-
})
|
|
214
|
-
.catch((uploadErrorRes) => {
|
|
215
|
-
console.log(uploadErrorRes);
|
|
216
|
-
newAttachment.Uploading = false;
|
|
217
|
-
this.setState({
|
|
218
|
-
noteAttachments: [...this.state.noteAttachments],
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
event.target.value = "";
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
onRemoveAttachment = (a) => {
|
|
225
|
-
const index = this.state.noteAttachments.indexOf(a);
|
|
226
|
-
if (index > -1) {
|
|
227
|
-
const newAttachments = [...this.state.noteAttachments];
|
|
228
|
-
newAttachments.splice(index, 1);
|
|
229
|
-
this.setState({
|
|
230
|
-
noteAttachments: newAttachments,
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
onShowSelectAssignee = () => {
|
|
236
|
-
this.setState({
|
|
237
|
-
showingAssigneeSelector: true,
|
|
238
|
-
});
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
onCloseSelectAssignee = () => {
|
|
242
|
-
this.setState({
|
|
243
|
-
showingAssigneeSelector: false,
|
|
244
|
-
});
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
onSelectAssignee = (user) => {
|
|
248
|
-
this.setState({
|
|
249
|
-
selectedAssignee: user,
|
|
250
|
-
});
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
onConfirmAssignee = async () => {
|
|
254
|
-
this.setState({
|
|
255
|
-
confirmingAssignee: true,
|
|
256
|
-
});
|
|
257
|
-
try {
|
|
258
|
-
if (this.state.selectedAssignee) {
|
|
259
|
-
await this.onAssignUser(this.state.selectedAssignee.id);
|
|
260
|
-
}
|
|
261
|
-
this.onCloseSelectAssignee();
|
|
262
|
-
} catch (error) {
|
|
263
|
-
console.error("onConfirmAssignee", error);
|
|
264
|
-
}
|
|
265
|
-
this.setState({
|
|
266
|
-
confirmingAssignee: false,
|
|
267
|
-
});
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
// Method to handle user assignment
|
|
271
|
-
onAssignUser = async (userId) => {
|
|
272
|
-
try {
|
|
273
|
-
const res = await maintenanceActions.assignJob(this.state.jobId, userId);
|
|
274
|
-
this.getJob();
|
|
275
|
-
} catch (err) {
|
|
276
|
-
console.error("onAssignUser", err);
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
onConfirmAddNote = async () => {
|
|
281
|
-
if (!this.isReadyToSaveNote()) return;
|
|
282
|
-
|
|
283
|
-
try {
|
|
284
|
-
this.setState({ submittingNote: true });
|
|
285
|
-
const res = await (this.state.editingNote
|
|
286
|
-
? maintenanceActions.editNote(
|
|
287
|
-
this.state.jobId,
|
|
288
|
-
this.state.editingNote,
|
|
289
|
-
this.state.noteInput,
|
|
290
|
-
this.state.noteAttachments.map((a) => {
|
|
291
|
-
return {
|
|
292
|
-
Title: a.Title,
|
|
293
|
-
Source: a.Source,
|
|
294
|
-
};
|
|
295
|
-
}),
|
|
296
|
-
this.state.noteImages,
|
|
297
|
-
)
|
|
298
|
-
: maintenanceActions.addNote(
|
|
299
|
-
this.state.jobId,
|
|
300
|
-
this.state.noteInput,
|
|
301
|
-
this.state.noteAttachments.map((a) => {
|
|
302
|
-
return {
|
|
303
|
-
Title: a.Title,
|
|
304
|
-
Source: a.Source,
|
|
305
|
-
};
|
|
306
|
-
}),
|
|
307
|
-
this.state.noteImages,
|
|
308
|
-
));
|
|
309
|
-
this.setState(
|
|
310
|
-
{
|
|
311
|
-
job: res.data.job,
|
|
312
|
-
submittingNote: false,
|
|
313
|
-
addNoteOpen: false,
|
|
314
|
-
noteInput: "",
|
|
315
|
-
noteAttachments: [],
|
|
316
|
-
noteImages: [],
|
|
317
|
-
editingNote: null,
|
|
318
|
-
},
|
|
319
|
-
() => {
|
|
320
|
-
this.props.jobsLoaded([this.state.job]);
|
|
321
|
-
},
|
|
322
|
-
);
|
|
323
|
-
} catch (err) {
|
|
324
|
-
console.error("onConfirmAddNote", err);
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
onDeleteNote = (n) => {
|
|
329
|
-
if (!window.confirm(values.textAreYouSureYouWantToDeleteNote)) {
|
|
330
|
-
this.setState({ noteMenuOpen: null });
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
maintenanceActions.deleteNote(this.state.jobId, n.Id);
|
|
335
|
-
const newNotes = _.filter(this.state.job.Notes, (note) => note.Id !== n.Id);
|
|
336
|
-
const newJob = { ...this.state.job };
|
|
337
|
-
newJob.Notes = newNotes;
|
|
338
|
-
this.setState({ job: newJob, noteMenuOpen: null });
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
onOpenEditNote = (n) => {
|
|
342
|
-
this.setState(
|
|
343
|
-
{
|
|
344
|
-
noteAttachments: n.Attachments || [],
|
|
345
|
-
noteImages: n.Images || [],
|
|
346
|
-
noteInput: n.Note || "",
|
|
347
|
-
addNoteOpen: true,
|
|
348
|
-
editingNote: n.Id,
|
|
349
|
-
noteMenuOpen: null,
|
|
350
|
-
},
|
|
351
|
-
this.checkSetImage,
|
|
352
|
-
);
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
markSeen = () => {
|
|
356
|
-
const { job } = this.state;
|
|
357
|
-
const { auth } = this.props;
|
|
358
|
-
// Must have maintenance permission and not the requester
|
|
359
|
-
if (
|
|
360
|
-
!Session.validateAccess(
|
|
361
|
-
auth.site,
|
|
362
|
-
values.permissionMaintenanceTracking,
|
|
363
|
-
auth,
|
|
364
|
-
)
|
|
365
|
-
)
|
|
366
|
-
return;
|
|
367
|
-
if (auth.user.Id === job.userID) return;
|
|
368
|
-
|
|
369
|
-
this.setState({ updating: true }, async () => {
|
|
370
|
-
try {
|
|
371
|
-
const update = {
|
|
372
|
-
id: job.id,
|
|
373
|
-
seen: true,
|
|
374
|
-
status: job.status || "Unassigned",
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
await maintenanceActions.editJob(update, auth.site);
|
|
378
|
-
} catch (error) {
|
|
379
|
-
this.setState({ updating: false });
|
|
380
|
-
console.log("markSeen error", error);
|
|
381
|
-
alert("Something went wrong with the request. Please try again.");
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
getComments = () => {
|
|
387
|
-
reactionActions
|
|
388
|
-
.getComments(this.state.jobId, values.commentKey, 0)
|
|
389
|
-
.then((res) => {
|
|
390
|
-
this.setState({ comments: res.data });
|
|
391
|
-
});
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
checkSetImage() {
|
|
395
|
-
if (this.imageInput && !_.isEmpty(this.state.noteImages)) {
|
|
396
|
-
this.imageInput.setValue(this.state.noteImages);
|
|
397
|
-
} else {
|
|
398
|
-
setTimeout(this.checkSetImage, 100);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
onAddComment = async () => {
|
|
403
|
-
const { commentInput, jobId, job, comments } = this.state;
|
|
404
|
-
try {
|
|
405
|
-
this.setState({ commentInput: "" });
|
|
406
|
-
const res = await reactionActions.addComment(
|
|
407
|
-
jobId,
|
|
408
|
-
values.commentKey,
|
|
409
|
-
job.title,
|
|
410
|
-
job.site,
|
|
411
|
-
commentInput,
|
|
412
|
-
);
|
|
413
|
-
this.setState({ comments: [...comments, res.data] });
|
|
414
|
-
} catch (error) {
|
|
415
|
-
console.error("onAddComment", error);
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
onHandleChange = (event) => {
|
|
420
|
-
var stateChange = {};
|
|
421
|
-
stateChange[event.target.getAttribute("id")] = event.target.value;
|
|
422
|
-
this.setState(stateChange);
|
|
423
|
-
};
|
|
424
|
-
|
|
425
|
-
onTogglePriorityChanger = () => {
|
|
426
|
-
this.setState({ priorityChangerOpen: !this.state.priorityChangerOpen });
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
onSelectPriority = async (priority) => {
|
|
430
|
-
this.setState({
|
|
431
|
-
job: { ...this.state.job, priority },
|
|
432
|
-
priorityChangerOpen: false,
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
try {
|
|
436
|
-
const res = await maintenanceActions.editJobPriority(
|
|
437
|
-
this.state.job.id,
|
|
438
|
-
priority,
|
|
439
|
-
);
|
|
440
|
-
const { job } = res.data;
|
|
441
|
-
this.props.jobsLoaded([job]);
|
|
442
|
-
this.setState({ job });
|
|
443
|
-
} catch (error) {
|
|
444
|
-
console.error("onSelectPriority", error);
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
onToggleStatusChanger = () => {
|
|
449
|
-
this.setState({ statusChangerOpen: !this.state.statusChangerOpen });
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
onSelectStatus = async (status) => {
|
|
453
|
-
this.setState({
|
|
454
|
-
job: {
|
|
455
|
-
...this.state.job,
|
|
456
|
-
status: status,
|
|
457
|
-
},
|
|
458
|
-
statusChangerOpen: false,
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
try {
|
|
462
|
-
const res = await maintenanceActions.editJobStatus(
|
|
463
|
-
this.state.job.id,
|
|
464
|
-
status,
|
|
465
|
-
);
|
|
466
|
-
const { job } = res.data;
|
|
467
|
-
this.props.jobsLoaded([job]);
|
|
468
|
-
this.setState({ job });
|
|
469
|
-
} catch (error) {
|
|
470
|
-
console.error("onSelectStatus", error);
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
renderPriorityChanger() {
|
|
475
|
-
if (!this.state.priorityChangerOpen) return null;
|
|
476
|
-
return (
|
|
477
|
-
<div className="statusChanger statusChanger-priority">
|
|
478
|
-
{jobPriorityOptions.map((p) => {
|
|
479
|
-
return (
|
|
480
|
-
<div
|
|
481
|
-
key={p.name}
|
|
482
|
-
className="statusLabel"
|
|
483
|
-
onClick={() => this.onSelectPriority(p.name)}
|
|
484
|
-
style={{ backgroundColor: p.color }}
|
|
485
|
-
>
|
|
486
|
-
<span className="statusLabel_text">{p.name}</span>
|
|
487
|
-
</div>
|
|
488
|
-
);
|
|
489
|
-
})}
|
|
490
|
-
</div>
|
|
491
|
-
);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
renderPriorityLabel() {
|
|
495
|
-
const { auth } = this.props;
|
|
496
|
-
if (
|
|
497
|
-
!Session.validateAccess(
|
|
498
|
-
auth.site,
|
|
499
|
-
values.permissionMaintenanceTracking,
|
|
500
|
-
auth,
|
|
501
|
-
)
|
|
502
|
-
)
|
|
503
|
-
return null;
|
|
504
|
-
|
|
505
|
-
const { job } = this.state;
|
|
506
|
-
if (!job) return null;
|
|
507
|
-
|
|
508
|
-
const selectedPriority = getJobPriority(job.priority);
|
|
509
|
-
return (
|
|
510
|
-
<div
|
|
511
|
-
className="statusLabel marginTop-5 pointer"
|
|
512
|
-
onClick={this.onTogglePriorityChanger}
|
|
513
|
-
style={{ backgroundColor: selectedPriority.color }}
|
|
514
|
-
>
|
|
515
|
-
<span className="statusLabel_text">
|
|
516
|
-
{job.priority || selectedPriority.name}
|
|
517
|
-
</span>
|
|
518
|
-
{this.renderPriorityChanger()}
|
|
519
|
-
</div>
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
renderStatusLabel() {
|
|
524
|
-
if (!this.state.job.status) return null;
|
|
525
|
-
const statusType = this.getStatusType(this.state.job.status);
|
|
526
|
-
const { auth } = this.props;
|
|
527
|
-
if (
|
|
528
|
-
Session.validateAccess(
|
|
529
|
-
auth.site,
|
|
530
|
-
values.permissionMaintenanceTracking,
|
|
531
|
-
auth,
|
|
532
|
-
)
|
|
533
|
-
) {
|
|
534
|
-
return (
|
|
535
|
-
<div
|
|
536
|
-
className="statusLabel pointer"
|
|
537
|
-
onClick={this.onToggleStatusChanger}
|
|
538
|
-
style={{ backgroundColor: statusType.color }}
|
|
539
|
-
>
|
|
540
|
-
<span className="statusLabel_text">{statusType.text}</span>
|
|
541
|
-
{this.renderStatusChanger()}
|
|
542
|
-
</div>
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
return (
|
|
546
|
-
<div
|
|
547
|
-
className="statusLabel"
|
|
548
|
-
style={{ backgroundColor: statusType.color }}
|
|
549
|
-
>
|
|
550
|
-
<span className="statusLabel_text">{statusType.text}</span>
|
|
551
|
-
</div>
|
|
552
|
-
);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
renderNotesButton() {
|
|
556
|
-
const { auth } = this.props;
|
|
557
|
-
if (
|
|
558
|
-
!Session.validateAccess(
|
|
559
|
-
auth.site,
|
|
560
|
-
values.permissionMaintenanceTracking,
|
|
561
|
-
auth,
|
|
562
|
-
)
|
|
563
|
-
)
|
|
564
|
-
return null;
|
|
565
|
-
return (
|
|
566
|
-
<div
|
|
567
|
-
className="statusLabel pointer"
|
|
568
|
-
onClick={this.onOpenAddNote}
|
|
569
|
-
style={{
|
|
570
|
-
backgroundColor: Config.env.colourBrandingMain,
|
|
571
|
-
marginLeft: 8,
|
|
572
|
-
}}
|
|
573
|
-
>
|
|
574
|
-
<span className="statusLabel_text">Add Note</span>
|
|
575
|
-
</div>
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
renderAssignButton() {
|
|
580
|
-
const { auth } = this.props;
|
|
581
|
-
if (
|
|
582
|
-
!Session.validateAccess(
|
|
583
|
-
auth.site,
|
|
584
|
-
values.permissionMaintenanceTracking,
|
|
585
|
-
auth,
|
|
586
|
-
)
|
|
587
|
-
)
|
|
588
|
-
return null;
|
|
589
|
-
return (
|
|
590
|
-
<div
|
|
591
|
-
className="statusLabel pointer"
|
|
592
|
-
onClick={this.onShowSelectAssignee}
|
|
593
|
-
style={{
|
|
594
|
-
backgroundColor: Config.env.colourBrandingMain,
|
|
595
|
-
marginLeft: 8,
|
|
596
|
-
}}
|
|
597
|
-
>
|
|
598
|
-
<span className="statusLabel_text">Assign</span>
|
|
599
|
-
</div>
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
renderStatusChanger() {
|
|
604
|
-
if (!this.state.statusChangerOpen) return null;
|
|
605
|
-
const { statusTypes } = this.props;
|
|
606
|
-
|
|
607
|
-
return (
|
|
608
|
-
<div className="statusChanger statusChanger-maintenance">
|
|
609
|
-
{statusTypes.map((status) => {
|
|
610
|
-
return (
|
|
611
|
-
<div
|
|
612
|
-
key={status.text}
|
|
613
|
-
className="statusLabel"
|
|
614
|
-
onClick={() => this.onSelectStatus(status.text)}
|
|
615
|
-
style={{ backgroundColor: status.color }}
|
|
616
|
-
>
|
|
617
|
-
<span className="statusLabel_text">{status.text}</span>
|
|
618
|
-
</div>
|
|
619
|
-
);
|
|
620
|
-
})}
|
|
621
|
-
</div>
|
|
622
|
-
);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
renderComment(c) {
|
|
626
|
-
return <Components.Comment key={c.Id} comment={c} />;
|
|
627
|
-
return (
|
|
628
|
-
<div key={c.Id} className="comment">
|
|
629
|
-
<p className="comment_text">{Helper.toParagraphed(c.Comment)}</p>
|
|
630
|
-
<div className="comment_bottom">
|
|
631
|
-
<Components.ProfilePic
|
|
632
|
-
className="comment_profilePic"
|
|
633
|
-
size={25}
|
|
634
|
-
image={c.User.profilePic}
|
|
635
|
-
/>
|
|
636
|
-
<p className="comment_name">{c.User.displayName}</p>
|
|
637
|
-
<p className="comment_time">
|
|
638
|
-
{moment.utc(c.Timestamp).local().format("D MMM YYYY • h:mma")}
|
|
639
|
-
</p>
|
|
640
|
-
</div>
|
|
641
|
-
</div>
|
|
642
|
-
);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
renderCommentSection() {
|
|
646
|
-
if (this.state.loadingComments) return null;
|
|
647
|
-
|
|
648
|
-
return (
|
|
649
|
-
<div className="padding-60 paddingLeft-20">
|
|
650
|
-
<div className="newTopBar paddingLeft-40">
|
|
651
|
-
<Components.Text type="formTitleSmall" className="marginBottom-16">
|
|
652
|
-
Comments
|
|
653
|
-
</Components.Text>
|
|
654
|
-
<div className="commentSection">
|
|
655
|
-
{this.state.comments.map((c) => this.renderComment(c))}
|
|
656
|
-
</div>
|
|
657
|
-
<div className="commentReply">
|
|
658
|
-
<div
|
|
659
|
-
className={`commentReply_button${!_.isEmpty(this.state.commentInput) ? " commentReply_button-active" : ""}`}
|
|
660
|
-
onClick={this.onAddComment}
|
|
661
|
-
>
|
|
662
|
-
<FontAwesome className="commentReply_icon" name="paper-plane-o" />
|
|
663
|
-
</div>
|
|
664
|
-
<Textarea
|
|
665
|
-
id="commentInput"
|
|
666
|
-
placeholder="Reply here..."
|
|
667
|
-
type="text"
|
|
668
|
-
className="commentReply_input"
|
|
669
|
-
value={this.state.commentInput}
|
|
670
|
-
onChange={(e) => this.onHandleChange(e)}
|
|
671
|
-
/>
|
|
672
|
-
</div>
|
|
673
|
-
</div>
|
|
674
|
-
</div>
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
renderImageGrid(images, size = undefined) {
|
|
679
|
-
const imagesToUse = images && images.length > 0 ? images : [];
|
|
680
|
-
return (
|
|
681
|
-
<div className="imageGrid">
|
|
682
|
-
{imagesToUse.map((image, i) => {
|
|
683
|
-
return (
|
|
684
|
-
<a href={image} target="_blank" rel="noopener noreferrer" key={i}>
|
|
685
|
-
<div
|
|
686
|
-
className="imageGrid_image"
|
|
687
|
-
style={{
|
|
688
|
-
backgroundImage: `url('${Helper.get1400(image)}')`,
|
|
689
|
-
width: size,
|
|
690
|
-
height: size,
|
|
691
|
-
}}
|
|
692
|
-
></div>
|
|
693
|
-
</a>
|
|
694
|
-
);
|
|
695
|
-
})}
|
|
696
|
-
</div>
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
renderDocumentGrid(documents) {
|
|
701
|
-
const documentsToUse = documents && documents.length > 0 ? documents : [];
|
|
702
|
-
return (
|
|
703
|
-
<div className="documentGrid">
|
|
704
|
-
{documentsToUse.map((doc, index) => (
|
|
705
|
-
<Components.Attachment
|
|
706
|
-
key={index}
|
|
707
|
-
uploading={doc.uploading}
|
|
708
|
-
source={doc.url}
|
|
709
|
-
title={doc.name}
|
|
710
|
-
/>
|
|
711
|
-
))}
|
|
712
|
-
</div>
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
renderImages() {
|
|
717
|
-
if (_.isEmpty(this.state.job.image) && _.isEmpty(this.state.job.images))
|
|
718
|
-
return null;
|
|
719
|
-
|
|
720
|
-
const imagesToUse = _.isEmpty(this.state.job.image)
|
|
721
|
-
? this.state.job.images
|
|
722
|
-
: [this.state.job.image];
|
|
723
|
-
return (
|
|
724
|
-
<div className="padding-60 paddingVertical-40 bottomDivideBorder">
|
|
725
|
-
<Components.Text type="formTitleSmall" className="marginBottom-16">
|
|
726
|
-
Images
|
|
727
|
-
</Components.Text>
|
|
728
|
-
{this.renderImageGrid(imagesToUse)}
|
|
729
|
-
</div>
|
|
730
|
-
);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
renderCustomFields() {
|
|
734
|
-
const { job } = this.state;
|
|
735
|
-
const { customFields } = job;
|
|
736
|
-
|
|
737
|
-
const labelClass = "fieldLabel";
|
|
738
|
-
const answerClass = "fontRegular fontSize-16 text-dark marginTop-5";
|
|
739
|
-
|
|
740
|
-
const renderAnswer = (field) => {
|
|
741
|
-
switch (field.type) {
|
|
742
|
-
case "date":
|
|
743
|
-
return (
|
|
744
|
-
<div className={answerClass}>
|
|
745
|
-
{field.answer
|
|
746
|
-
? moment(field.answer, "YYYY-MM-DD").format("DD-MMM-YYYY")
|
|
747
|
-
: ""}
|
|
748
|
-
</div>
|
|
749
|
-
);
|
|
750
|
-
case "time":
|
|
751
|
-
return (
|
|
752
|
-
<div className={answerClass}>
|
|
753
|
-
{field.answer
|
|
754
|
-
? moment(field.answer, "HH:mm").format("h:mm a")
|
|
755
|
-
: ""}
|
|
756
|
-
</div>
|
|
757
|
-
);
|
|
758
|
-
case "yn":
|
|
759
|
-
return (
|
|
760
|
-
<div className={answerClass}>{field.answer ? "Yes" : "No"}</div>
|
|
761
|
-
);
|
|
762
|
-
case "checkbox":
|
|
763
|
-
return (
|
|
764
|
-
<div className={answerClass}>
|
|
765
|
-
{field.answer && Array.isArray(field.answer)
|
|
766
|
-
? field.answer.join(", ")
|
|
767
|
-
: ""}
|
|
768
|
-
</div>
|
|
769
|
-
);
|
|
770
|
-
case "image":
|
|
771
|
-
return this.renderImageGrid(field.answer);
|
|
772
|
-
case "document":
|
|
773
|
-
return this.renderDocumentGrid(field.answer);
|
|
774
|
-
default:
|
|
775
|
-
return <div className={answerClass}>{field.answer}</div>;
|
|
776
|
-
}
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
return (
|
|
780
|
-
<div className="padding-60 paddingVertical-40 bottomDivideBorder">
|
|
781
|
-
{customFields.map((field, index) => {
|
|
782
|
-
if (["staticTitle", "staticText"].includes(field.type)) return null;
|
|
783
|
-
if (
|
|
784
|
-
_.isNil(field.answer) ||
|
|
785
|
-
field.answer === "" ||
|
|
786
|
-
field.answer.length === 0
|
|
787
|
-
)
|
|
788
|
-
return null;
|
|
789
|
-
return (
|
|
790
|
-
<div key={index} className="marginTop-16">
|
|
791
|
-
<div className={labelClass}>{field.label}</div>
|
|
792
|
-
{renderAnswer(field)}
|
|
793
|
-
</div>
|
|
794
|
-
);
|
|
795
|
-
})}
|
|
796
|
-
</div>
|
|
797
|
-
);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
renderInner() {
|
|
801
|
-
if (this.state.job == null) return null;
|
|
802
|
-
const { customFields } = this.state.job;
|
|
803
|
-
const hasCustomFields = customFields && customFields.length > 0;
|
|
804
|
-
|
|
805
|
-
return (
|
|
806
|
-
<div style={{ paddingBottom: 40 }}>
|
|
807
|
-
<div className="padding-60 paddingVertical-40 bottomDivideBorder relative">
|
|
808
|
-
<Components.Text type="formTitleLarge" className="marginBottom-8">
|
|
809
|
-
{this.state.job.title || values.textSingularName}
|
|
810
|
-
</Components.Text>
|
|
811
|
-
<Components.Text type="formTitleMedium" className="marginBottom-24">
|
|
812
|
-
{values.textEntityName} #{this.state.job.jobId}
|
|
813
|
-
</Components.Text>
|
|
814
|
-
<div className="marginTop-16">
|
|
815
|
-
<div className={"fieldLabel"}>Submission date</div>
|
|
816
|
-
<div className={"fontRegular fontSize-16 text-dark marginTop-5"}>
|
|
817
|
-
{moment
|
|
818
|
-
.utc(this.state.job.createdTime)
|
|
819
|
-
.local()
|
|
820
|
-
.format("D MMM YY")}
|
|
821
|
-
</div>
|
|
822
|
-
</div>
|
|
823
|
-
<div className="marginTop-16">
|
|
824
|
-
<div className={"fieldLabel"}>Type</div>
|
|
825
|
-
<div className={"fontRegular fontSize-16 text-dark marginTop-5"}>
|
|
826
|
-
{this.state.job.type}
|
|
827
|
-
</div>
|
|
828
|
-
</div>
|
|
829
|
-
<div className="marginTop-16">
|
|
830
|
-
<div className={"fieldLabel"}>Address</div>
|
|
831
|
-
<div className={"fontRegular fontSize-16 text-dark marginTop-5"}>
|
|
832
|
-
{this.state.job.room}
|
|
833
|
-
</div>
|
|
834
|
-
</div>
|
|
835
|
-
{hasCustomFields ? null : (
|
|
836
|
-
<div className="marginTop-16">
|
|
837
|
-
<div className={"fieldLabel"}>
|
|
838
|
-
Description {this.state.job.image ? "- (image supplied)" : ""}
|
|
839
|
-
</div>
|
|
840
|
-
<div className={"fontRegular fontSize-16 text-dark marginTop-5"}>
|
|
841
|
-
{this.state.job.description}
|
|
842
|
-
</div>
|
|
843
|
-
</div>
|
|
844
|
-
)}
|
|
845
|
-
</div>
|
|
846
|
-
<div className="padding-60 paddingVertical-40 bottomDivideBorder">
|
|
847
|
-
<Components.Text type="formTitleSmall" className="marginBottom-16">
|
|
848
|
-
Contact Details
|
|
849
|
-
</Components.Text>
|
|
850
|
-
<div className="marginTop-16">
|
|
851
|
-
<div className={"fieldLabel"}>Name</div>
|
|
852
|
-
<div className={"fontRegular fontSize-16 text-dark marginTop-5"}>
|
|
853
|
-
{this.state.job.userName}
|
|
854
|
-
</div>
|
|
855
|
-
</div>
|
|
856
|
-
<div className="marginTop-16">
|
|
857
|
-
<div className={"fieldLabel"}>Contact number</div>
|
|
858
|
-
<div className={"fontRegular fontSize-16 text-dark marginTop-5"}>
|
|
859
|
-
{_.isEmpty(this.state.job.phone)
|
|
860
|
-
? "No phone provided"
|
|
861
|
-
: this.state.job.phone}
|
|
862
|
-
</div>
|
|
863
|
-
</div>
|
|
864
|
-
{hasCustomFields ? null : (
|
|
865
|
-
<div>
|
|
866
|
-
<div className="marginTop-16">
|
|
867
|
-
<div className={"fieldLabel"}>Should person be home?</div>
|
|
868
|
-
<div
|
|
869
|
-
className={"fontRegular fontSize-16 text-dark marginTop-5"}
|
|
870
|
-
>
|
|
871
|
-
{this.state.job.isHome ? "Yes" : "No"}
|
|
872
|
-
</div>
|
|
873
|
-
</div>
|
|
874
|
-
{this.state.job.isHome && this.state.job.homeText && (
|
|
875
|
-
<div className="marginTop-16">
|
|
876
|
-
<div className={"fieldLabel"}>When</div>
|
|
877
|
-
<div
|
|
878
|
-
className={"fontRegular fontSize-16 text-dark marginTop-5"}
|
|
879
|
-
>
|
|
880
|
-
{this.state.job.homeText}
|
|
881
|
-
</div>
|
|
882
|
-
</div>
|
|
883
|
-
)}
|
|
884
|
-
</div>
|
|
885
|
-
)}
|
|
886
|
-
</div>
|
|
887
|
-
{hasCustomFields ? null : this.renderImages()}
|
|
888
|
-
{hasCustomFields ? this.renderCustomFields() : null}
|
|
889
|
-
{this.renderCommentSection()}
|
|
890
|
-
</div>
|
|
891
|
-
);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
renderHistoryEntry(e, i) {
|
|
895
|
-
const { job } = this.state;
|
|
896
|
-
const entryToUse = e || {
|
|
897
|
-
timestamp: job.createdTime,
|
|
898
|
-
status: "Unassigned",
|
|
899
|
-
user: {
|
|
900
|
-
displayName: job.userName,
|
|
901
|
-
id: job.userID,
|
|
902
|
-
profilePic: job.userProfilePic,
|
|
903
|
-
},
|
|
904
|
-
};
|
|
905
|
-
const statusType = this.getStatusType(entryToUse.status);
|
|
906
|
-
return (
|
|
907
|
-
<div className="ticketHistoryEntry" key={i}>
|
|
908
|
-
<p className="ticketHistoryEntry_timestamp">
|
|
909
|
-
{moment.utc(entryToUse.timestamp).local().format("D MMM YYYY h:mma")}
|
|
910
|
-
</p>
|
|
911
|
-
<div
|
|
912
|
-
className="statusLabel statusLabel-large statusLabel-full"
|
|
913
|
-
style={{ backgroundColor: statusType.color }}
|
|
914
|
-
>
|
|
915
|
-
<span className="statusLabel_text">
|
|
916
|
-
{e
|
|
917
|
-
? `${entryToUse.user.displayName} marked as ${statusType.text}`
|
|
918
|
-
: `${values.textEntityName} opened`}
|
|
919
|
-
</span>
|
|
920
|
-
</div>
|
|
921
|
-
</div>
|
|
922
|
-
);
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
renderNote(note, index) {
|
|
926
|
-
return (
|
|
927
|
-
<div className="ticketHistoryEntry" key={index}>
|
|
928
|
-
<p className="ticketHistoryEntry_timestamp">
|
|
929
|
-
{moment.utc(note.Timestamp).local().format("D MMM YYYY h:mma")}
|
|
930
|
-
</p>
|
|
931
|
-
<div
|
|
932
|
-
className="statusLabel statusLabel-large statusLabel-full"
|
|
933
|
-
style={{ backgroundColor: "#6e79c5" }}
|
|
934
|
-
>
|
|
935
|
-
<span className="statusLabel_text">Staff Notes</span>
|
|
936
|
-
</div>
|
|
937
|
-
<div className="maintenanceNote">
|
|
938
|
-
<div className="maintenanceNote_top">
|
|
939
|
-
{this.props.auth &&
|
|
940
|
-
this.props.auth.user &&
|
|
941
|
-
this.props.auth.user.Id === note.User.id && (
|
|
942
|
-
<Components.SVGIcon
|
|
943
|
-
colour={Colours.COLOUR_DUSK_LIGHT}
|
|
944
|
-
icon="more15"
|
|
945
|
-
className="maintenanceNote_moreIcon"
|
|
946
|
-
onClick={() => this.onOpenNoteMenu(index)}
|
|
947
|
-
/>
|
|
948
|
-
)}
|
|
949
|
-
<p className="maintenanceNote_name">{note.User.displayName}</p>
|
|
950
|
-
{this.state.noteMenuOpen === index && (
|
|
951
|
-
<Components.MoreMenu
|
|
952
|
-
options={[
|
|
953
|
-
{
|
|
954
|
-
key: "edit",
|
|
955
|
-
text: "Edit",
|
|
956
|
-
onPress: () => this.onOpenEditNote(note),
|
|
957
|
-
},
|
|
958
|
-
{
|
|
959
|
-
key: "delete",
|
|
960
|
-
text: "Delete",
|
|
961
|
-
onPress: () => this.onDeleteNote(note),
|
|
962
|
-
},
|
|
963
|
-
]}
|
|
964
|
-
/>
|
|
965
|
-
)}
|
|
966
|
-
</div>
|
|
967
|
-
<p className="maintenanceNote_text">
|
|
968
|
-
{Helper.toParagraphed(note.Note)}
|
|
969
|
-
</p>
|
|
970
|
-
{note.Attachments.map((a, i) => this.renderAttachment(a, i))}
|
|
971
|
-
{note.Images && note.Images.length > 0
|
|
972
|
-
? this.renderImageGrid(note.Images, IMAGE_SIZE_NOTE)
|
|
973
|
-
: null}
|
|
974
|
-
</div>
|
|
975
|
-
</div>
|
|
976
|
-
);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
renderAssignment() {
|
|
980
|
-
const { job } = this.state;
|
|
981
|
-
if (!job) return null;
|
|
982
|
-
|
|
983
|
-
return (
|
|
984
|
-
<div className="padding-32 paddingVertical-40 bottomDivideBorder relative">
|
|
985
|
-
<div className="newTopBar clearfix flex flex-reverse">
|
|
986
|
-
{this.renderAssignButton()}
|
|
987
|
-
<Components.Text type="formTitleSmall" className="flex-1">
|
|
988
|
-
Assignment
|
|
989
|
-
</Components.Text>
|
|
990
|
-
</div>
|
|
991
|
-
<div>
|
|
992
|
-
<div className="marginTop-16">
|
|
993
|
-
<div className={"fieldLabel"}>Assigned to</div>
|
|
994
|
-
<div className={"fontRegular fontSize-16 text-dark marginTop-5"}>
|
|
995
|
-
{job.Assignee ? (
|
|
996
|
-
<Components.UserListing user={job.Assignee} />
|
|
997
|
-
) : (
|
|
998
|
-
"Unassigned"
|
|
999
|
-
)}
|
|
1000
|
-
</div>
|
|
1001
|
-
</div>
|
|
1002
|
-
</div>
|
|
1003
|
-
</div>
|
|
1004
|
-
);
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
renderAssignmentEntry(e, i) {
|
|
1008
|
-
return (
|
|
1009
|
-
<div className="ticketHistoryEntry" key={i}>
|
|
1010
|
-
<p className="ticketHistoryEntry_timestamp">
|
|
1011
|
-
{moment.utc(e.timestamp).local().format("D MMM YYYY h:mma")}
|
|
1012
|
-
</p>
|
|
1013
|
-
<div
|
|
1014
|
-
className="statusLabel statusLabel-large statusLabel-full"
|
|
1015
|
-
style={{ backgroundColor: Colours.COLOUR_DUSK }}
|
|
1016
|
-
>
|
|
1017
|
-
<span className="statusLabel_text">
|
|
1018
|
-
{e.user.displayName} assigned the {values.textSingularName} to{" "}
|
|
1019
|
-
{e.assignedUser ? e.assignedUser.displayName : "Unassigned"}
|
|
1020
|
-
</span>
|
|
1021
|
-
</div>
|
|
1022
|
-
</div>
|
|
1023
|
-
);
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
renderExternalSyncEntry(e, i) {
|
|
1027
|
-
const isSuccess = e.EntryType === "ExternalIDSet";
|
|
1028
|
-
const backgroundColor = isSuccess
|
|
1029
|
-
? Colours.COLOUR_GREEN
|
|
1030
|
-
: Colours.COLOUR_RED; // Green for success, red for failure
|
|
1031
|
-
|
|
1032
|
-
return (
|
|
1033
|
-
<div className="ticketHistoryEntry" key={i}>
|
|
1034
|
-
<p className="ticketHistoryEntry_timestamp">
|
|
1035
|
-
{moment.utc(e.timestamp).local().format("D MMM YYYY h:mma")}
|
|
1036
|
-
</p>
|
|
1037
|
-
<div
|
|
1038
|
-
className="statusLabel statusLabel-large statusLabel-full"
|
|
1039
|
-
style={{ backgroundColor }}
|
|
1040
|
-
>
|
|
1041
|
-
<span className="statusLabel_text">
|
|
1042
|
-
{isSuccess
|
|
1043
|
-
? `Synced to ${e.systemType || "external system"}${e.externalId ? ` (ID: ${e.externalId})` : ""}`
|
|
1044
|
-
: `Failed to sync to ${e.systemType || "external system"}${e.error ? `: ${e.error}` : ""}`}
|
|
1045
|
-
</span>
|
|
1046
|
-
</div>
|
|
1047
|
-
</div>
|
|
1048
|
-
);
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
renderPriority() {
|
|
1052
|
-
const { auth } = this.props;
|
|
1053
|
-
if (
|
|
1054
|
-
!Session.validateAccess(
|
|
1055
|
-
auth.site,
|
|
1056
|
-
values.permissionMaintenanceTracking,
|
|
1057
|
-
auth,
|
|
1058
|
-
)
|
|
1059
|
-
)
|
|
1060
|
-
return null;
|
|
1061
|
-
|
|
1062
|
-
return (
|
|
1063
|
-
<div className="padding-32 paddingVertical-40 bottomDivideBorder relative">
|
|
1064
|
-
<div className="newTopBar clearfix flex flex-reverse">
|
|
1065
|
-
{this.renderPriorityLabel()}
|
|
1066
|
-
<Components.Text type="formTitleSmall" className="flex-1">
|
|
1067
|
-
Priority
|
|
1068
|
-
</Components.Text>
|
|
1069
|
-
</div>
|
|
1070
|
-
</div>
|
|
1071
|
-
);
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
hasSyncFailureWithoutSuccess() {
|
|
1075
|
-
const { job } = this.state;
|
|
1076
|
-
if (!job || !job.history) return false;
|
|
1077
|
-
|
|
1078
|
-
const history = job.history || [];
|
|
1079
|
-
const hasSuccess = history.some(
|
|
1080
|
-
(entry) => entry.EntryType === "ExternalIDSet",
|
|
1081
|
-
);
|
|
1082
|
-
const hasFailure = history.some(
|
|
1083
|
-
(entry) => entry.EntryType === "ExternalIDSetFailed",
|
|
1084
|
-
);
|
|
1085
|
-
|
|
1086
|
-
return hasFailure && !hasSuccess;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
renderRetrySyncButton() {
|
|
1090
|
-
const { auth } = this.props;
|
|
1091
|
-
const { retryingSync, retrySyncInitiated } = this.state;
|
|
1092
|
-
|
|
1093
|
-
// Only show for users with maintenance tracking permission
|
|
1094
|
-
if (
|
|
1095
|
-
!Session.validateAccess(
|
|
1096
|
-
auth.site,
|
|
1097
|
-
values.permissionMaintenanceTracking,
|
|
1098
|
-
auth,
|
|
1099
|
-
)
|
|
1100
|
-
)
|
|
1101
|
-
return null;
|
|
1102
|
-
|
|
1103
|
-
// Only show if there's a failure without success and retry hasn't been initiated
|
|
1104
|
-
if (!this.hasSyncFailureWithoutSuccess() || retrySyncInitiated) return null;
|
|
1105
|
-
|
|
1106
|
-
// Show spinner while retrying
|
|
1107
|
-
if (retryingSync) {
|
|
1108
|
-
return (
|
|
1109
|
-
<FontAwesome
|
|
1110
|
-
style={{
|
|
1111
|
-
fontSize: 20,
|
|
1112
|
-
color: Colours.COLOUR_DUSK_LIGHT,
|
|
1113
|
-
marginLeft: 8,
|
|
1114
|
-
}}
|
|
1115
|
-
name="spinner fa-pulse fa-fw"
|
|
1116
|
-
/>
|
|
1117
|
-
);
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
return (
|
|
1121
|
-
<div
|
|
1122
|
-
className="statusLabel pointer"
|
|
1123
|
-
onClick={this.onRetrySync}
|
|
1124
|
-
style={{ backgroundColor: Colours.COLOUR_RED, marginLeft: 8 }}
|
|
1125
|
-
>
|
|
1126
|
-
<span className="statusLabel_text">Retry Sync</span>
|
|
1127
|
-
</div>
|
|
1128
|
-
);
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
renderExternalSyncStatus() {
|
|
1132
|
-
const { retrySyncError, retrySyncInitiated } = this.state;
|
|
1133
|
-
|
|
1134
|
-
// Show error message if retry failed
|
|
1135
|
-
if (retrySyncError) {
|
|
1136
|
-
return (
|
|
1137
|
-
<Components.Text type="body">
|
|
1138
|
-
<FontAwesome
|
|
1139
|
-
className="userStatusIcon"
|
|
1140
|
-
name="times-circle"
|
|
1141
|
-
style={{ color: Colours.COLOUR_RED }}
|
|
1142
|
-
/>{" "}
|
|
1143
|
-
{retrySyncError}
|
|
1144
|
-
</Components.Text>
|
|
1145
|
-
);
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// Show success message if retry was initiated
|
|
1149
|
-
if (retrySyncInitiated) {
|
|
1150
|
-
return (
|
|
1151
|
-
<Components.Text type="body">
|
|
1152
|
-
<FontAwesome
|
|
1153
|
-
className="userStatusIcon"
|
|
1154
|
-
name="check-circle"
|
|
1155
|
-
style={{ color: Colours.COLOUR_GREEN }}
|
|
1156
|
-
/>{" "}
|
|
1157
|
-
Sync retry initiated. Check back shortly for results.
|
|
1158
|
-
</Components.Text>
|
|
1159
|
-
);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
// Show failure message with instruction
|
|
1163
|
-
if (this.hasSyncFailureWithoutSuccess()) {
|
|
1164
|
-
return (
|
|
1165
|
-
<Components.Text type="body">
|
|
1166
|
-
<FontAwesome
|
|
1167
|
-
className="userStatusIcon"
|
|
1168
|
-
name="times-circle"
|
|
1169
|
-
style={{ color: Colours.COLOUR_RED }}
|
|
1170
|
-
/>{" "}
|
|
1171
|
-
External sync failed. Use the retry button to attempt again.
|
|
1172
|
-
</Components.Text>
|
|
1173
|
-
);
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
return null;
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
renderExternalSync() {
|
|
1180
|
-
const { externalSync, loadingExternalSync } = this.state;
|
|
1181
|
-
|
|
1182
|
-
// Check if we should show this section at all
|
|
1183
|
-
const hasExternalSyncData = externalSync && !loadingExternalSync;
|
|
1184
|
-
const hasSyncFailure = this.hasSyncFailureWithoutSuccess();
|
|
1185
|
-
|
|
1186
|
-
// Show section if we have sync data OR if there's a failure that can be retried
|
|
1187
|
-
if (!hasExternalSyncData && !hasSyncFailure) return null;
|
|
1188
|
-
|
|
1189
|
-
return (
|
|
1190
|
-
<div className="padding-32 paddingVertical-40 bottomDivideBorder relative">
|
|
1191
|
-
<div className="newTopBar clearfix flex flex-reverse">
|
|
1192
|
-
{this.renderRetrySyncButton()}
|
|
1193
|
-
<Components.Text type="formTitleSmall" className="flex-1">
|
|
1194
|
-
External Sync
|
|
1195
|
-
</Components.Text>
|
|
1196
|
-
</div>
|
|
1197
|
-
<div className="marginTop-16">
|
|
1198
|
-
{hasExternalSyncData ? (
|
|
1199
|
-
<>
|
|
1200
|
-
{externalSync.systemType && (
|
|
1201
|
-
<Components.Text type="body" className="marginBottom-8">
|
|
1202
|
-
<strong>System:</strong> {externalSync.systemType}
|
|
1203
|
-
</Components.Text>
|
|
1204
|
-
)}
|
|
1205
|
-
{externalSync.externalId && (
|
|
1206
|
-
<Components.Text type="body" className="marginBottom-8">
|
|
1207
|
-
<strong>External ID:</strong> {externalSync.externalId}
|
|
1208
|
-
</Components.Text>
|
|
1209
|
-
)}
|
|
1210
|
-
{externalSync.syncedAt && (
|
|
1211
|
-
<Components.Text type="body" className="marginBottom-8">
|
|
1212
|
-
<strong>Synced:</strong>{" "}
|
|
1213
|
-
{moment
|
|
1214
|
-
.utc(externalSync.syncedAt)
|
|
1215
|
-
.local()
|
|
1216
|
-
.format("D MMM YYYY h:mma")}
|
|
1217
|
-
</Components.Text>
|
|
1218
|
-
)}
|
|
1219
|
-
</>
|
|
1220
|
-
) : (
|
|
1221
|
-
this.renderExternalSyncStatus()
|
|
1222
|
-
)}
|
|
1223
|
-
</div>
|
|
1224
|
-
</div>
|
|
1225
|
-
);
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
renderOverview() {
|
|
1229
|
-
const { job } = this.state;
|
|
1230
|
-
if (!job || !job.history) return null;
|
|
1231
|
-
|
|
1232
|
-
const source = _.sortBy(
|
|
1233
|
-
[
|
|
1234
|
-
...job.history.map((e) => {
|
|
1235
|
-
return { ...e, EntryType: e.EntryType || "status" };
|
|
1236
|
-
}),
|
|
1237
|
-
...(job.Notes || []).map((e) => {
|
|
1238
|
-
return { ...e, timestamp: e.Timestamp, EntryType: "note" };
|
|
1239
|
-
}),
|
|
1240
|
-
],
|
|
1241
|
-
"timestamp",
|
|
1242
|
-
);
|
|
1243
|
-
|
|
1244
|
-
return (
|
|
1245
|
-
<div className="padding-32 paddingVertical-40 bottomDivideBorder relative">
|
|
1246
|
-
<div className="newTopBar clearfix flex flex-reverse">
|
|
1247
|
-
{this.renderNotesButton()}
|
|
1248
|
-
{this.renderStatusLabel()}
|
|
1249
|
-
<Components.Text type="formTitleSmall" className="flex-1">
|
|
1250
|
-
Status History
|
|
1251
|
-
</Components.Text>
|
|
1252
|
-
</div>
|
|
1253
|
-
{this.renderHistoryEntry(null, -1)}
|
|
1254
|
-
{_.map(source, (e, i) => {
|
|
1255
|
-
switch (e.EntryType) {
|
|
1256
|
-
case "status":
|
|
1257
|
-
return this.renderHistoryEntry(e, i);
|
|
1258
|
-
case "note":
|
|
1259
|
-
return this.renderNote(e, i);
|
|
1260
|
-
case "assignment":
|
|
1261
|
-
return this.renderAssignmentEntry(e, i);
|
|
1262
|
-
case "ExternalIDSet":
|
|
1263
|
-
case "ExternalIDSetFailed":
|
|
1264
|
-
return this.renderExternalSyncEntry(e, i);
|
|
1265
|
-
default:
|
|
1266
|
-
break;
|
|
1267
|
-
}
|
|
1268
|
-
})}
|
|
1269
|
-
</div>
|
|
1270
|
-
);
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
renderButtons() {
|
|
1274
|
-
return (
|
|
1275
|
-
<div>
|
|
1276
|
-
<Components.Button
|
|
1277
|
-
inline
|
|
1278
|
-
buttonType="tertiary"
|
|
1279
|
-
onClick={() => {
|
|
1280
|
-
window.history.back();
|
|
1281
|
-
}}
|
|
1282
|
-
isActive
|
|
1283
|
-
style={{ marginRight: 16 }}
|
|
1284
|
-
>
|
|
1285
|
-
Back
|
|
1286
|
-
</Components.Button>
|
|
1287
|
-
{Session.validateAccess(
|
|
1288
|
-
this.props.auth.site,
|
|
1289
|
-
values.permissionMaintenanceTracking,
|
|
1290
|
-
this.props.auth,
|
|
1291
|
-
) &&
|
|
1292
|
-
!_.isEmpty(this.state.job) && (
|
|
1293
|
-
<Link to={`${values.routeAddRequest}/${this.state.jobId}`}>
|
|
1294
|
-
<Components.Button
|
|
1295
|
-
inline
|
|
1296
|
-
style={{ marginRight: 25 }}
|
|
1297
|
-
buttonType="outlined"
|
|
1298
|
-
isActive
|
|
1299
|
-
onClick={this.editJob}
|
|
1300
|
-
>
|
|
1301
|
-
Edit Details
|
|
1302
|
-
</Components.Button>
|
|
1303
|
-
</Link>
|
|
1304
|
-
)}
|
|
1305
|
-
</div>
|
|
1306
|
-
);
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
renderAttachment(attachment, index, onRemove) {
|
|
1310
|
-
if (!attachment) return null;
|
|
1311
|
-
|
|
1312
|
-
return (
|
|
1313
|
-
<Components.Attachment
|
|
1314
|
-
key={index}
|
|
1315
|
-
uploading={attachment.Uploading}
|
|
1316
|
-
source={attachment.Source}
|
|
1317
|
-
title={attachment.Title}
|
|
1318
|
-
onRemove={onRemove ? () => onRemove(attachment) : undefined}
|
|
1319
|
-
/>
|
|
1320
|
-
);
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
renderAddNotePopup() {
|
|
1324
|
-
if (!this.state.addNoteOpen) return null;
|
|
1325
|
-
|
|
1326
|
-
if (this.state.submittingNote) {
|
|
1327
|
-
return (
|
|
1328
|
-
<Components.Popup title="Saving Note" maxWidth={600} hasPadding>
|
|
1329
|
-
<div className="flex flex-center-row">
|
|
1330
|
-
<FontAwesome className="spinner" name="spinner fa-pulse fa-fw" />
|
|
1331
|
-
</div>
|
|
1332
|
-
</Components.Popup>
|
|
1333
|
-
);
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
return (
|
|
1337
|
-
<Components.Popup
|
|
1338
|
-
title={`${this.state.editingNote ? "Edit" : "Add"} Note`}
|
|
1339
|
-
onClose={this.onCloseAddNote}
|
|
1340
|
-
maxWidth={600}
|
|
1341
|
-
hasPadding
|
|
1342
|
-
buttons={[
|
|
1343
|
-
{
|
|
1344
|
-
type: "primary",
|
|
1345
|
-
onClick: this.onConfirmAddNote,
|
|
1346
|
-
isActive: this.isReadyToSaveNote(),
|
|
1347
|
-
text: "Save",
|
|
1348
|
-
},
|
|
1349
|
-
{
|
|
1350
|
-
type: "tertiary",
|
|
1351
|
-
onClick: this.onCloseAddNote,
|
|
1352
|
-
isActive: true,
|
|
1353
|
-
text: "Cancel",
|
|
1354
|
-
},
|
|
1355
|
-
]}
|
|
1356
|
-
>
|
|
1357
|
-
<Components.GenericInput
|
|
1358
|
-
id="noteInput"
|
|
1359
|
-
type="textarea"
|
|
1360
|
-
componentClass="textarea"
|
|
1361
|
-
value={this.state.noteInput}
|
|
1362
|
-
placeholder="Enter note"
|
|
1363
|
-
onChange={(e) => this.onHandleChange(e)}
|
|
1364
|
-
inputStyle={{
|
|
1365
|
-
width: 400,
|
|
1366
|
-
}}
|
|
1367
|
-
/>
|
|
1368
|
-
<Components.Text type="h5">Attachments</Components.Text>
|
|
1369
|
-
{this.state.noteAttachments.map((a, i) =>
|
|
1370
|
-
this.renderAttachment(a, i, this.onRemoveAttachment),
|
|
1371
|
-
)}
|
|
1372
|
-
<input
|
|
1373
|
-
ref={(input) => (this.attachmentInput = input)}
|
|
1374
|
-
id="attachmentInput"
|
|
1375
|
-
type="file"
|
|
1376
|
-
className="fileInput"
|
|
1377
|
-
onChange={(e) => this.onHandlePDFFileChange(e)}
|
|
1378
|
-
accept="application/pdf"
|
|
1379
|
-
/>
|
|
1380
|
-
<div
|
|
1381
|
-
className="iconTextButton marginBottom-16"
|
|
1382
|
-
onClick={() => {
|
|
1383
|
-
this.attachmentInput.click();
|
|
1384
|
-
}}
|
|
1385
|
-
>
|
|
1386
|
-
<FontAwesome className="iconTextButton_icon" name="paperclip" />
|
|
1387
|
-
<p className="iconTextButton_text">Add Attachment</p>
|
|
1388
|
-
</div>
|
|
1389
|
-
<Components.ImageInput
|
|
1390
|
-
ref={(ref) => {
|
|
1391
|
-
this.imageInput = ref;
|
|
1392
|
-
}}
|
|
1393
|
-
multiple
|
|
1394
|
-
refreshCallback={(images) => {
|
|
1395
|
-
this.setState({ noteImages: images });
|
|
1396
|
-
}}
|
|
1397
|
-
/>
|
|
1398
|
-
</Components.Popup>
|
|
1399
|
-
);
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
renderUsers() {
|
|
1403
|
-
let content = null;
|
|
1404
|
-
if (this.state.confirmingAssignee) {
|
|
1405
|
-
content = (
|
|
1406
|
-
<div className="flex flex-center-row">
|
|
1407
|
-
<FontAwesome className="spinner" name="spinner fa-pulse fa-fw" />
|
|
1408
|
-
</div>
|
|
1409
|
-
);
|
|
1410
|
-
} else if (this.state.selectedAssignee) {
|
|
1411
|
-
content = (
|
|
1412
|
-
<div>
|
|
1413
|
-
<Components.UserListing
|
|
1414
|
-
key={this.state.selectedAssignee.id}
|
|
1415
|
-
user={this.state.selectedAssignee}
|
|
1416
|
-
rightContent={
|
|
1417
|
-
<Components.SVGIcon
|
|
1418
|
-
className="removeIcon"
|
|
1419
|
-
icon="close"
|
|
1420
|
-
onClick={() => {
|
|
1421
|
-
this.onSelectAssignee();
|
|
1422
|
-
}}
|
|
1423
|
-
colour={Colours.COLOUR_DUSK}
|
|
1424
|
-
/>
|
|
1425
|
-
}
|
|
1426
|
-
/>
|
|
1427
|
-
</div>
|
|
1428
|
-
);
|
|
1429
|
-
} else {
|
|
1430
|
-
content = (
|
|
1431
|
-
<div>
|
|
1432
|
-
<Components.GenericInput
|
|
1433
|
-
id="userSearch"
|
|
1434
|
-
type="text"
|
|
1435
|
-
// label="Search"
|
|
1436
|
-
placeholder="Search name"
|
|
1437
|
-
value={this.state.userSearch}
|
|
1438
|
-
onChange={(e) => this.onHandleChange(e)}
|
|
1439
|
-
alwaysShowLabel
|
|
1440
|
-
/>
|
|
1441
|
-
{_.sortBy(this.state.assignees, (u) => u.displayName.toUpperCase())
|
|
1442
|
-
.filter((u) => {
|
|
1443
|
-
if (_.isEmpty(this.state.userSearch)) return true;
|
|
1444
|
-
return (
|
|
1445
|
-
u.displayName
|
|
1446
|
-
.toUpperCase()
|
|
1447
|
-
.indexOf(this.state.userSearch.toUpperCase()) > -1
|
|
1448
|
-
);
|
|
1449
|
-
})
|
|
1450
|
-
.map((user) => {
|
|
1451
|
-
return (
|
|
1452
|
-
<Components.UserListing
|
|
1453
|
-
key={user.id}
|
|
1454
|
-
user={user}
|
|
1455
|
-
onClick={() => {
|
|
1456
|
-
this.onSelectAssignee(user);
|
|
1457
|
-
}}
|
|
1458
|
-
/>
|
|
1459
|
-
);
|
|
1460
|
-
})}
|
|
1461
|
-
</div>
|
|
1462
|
-
);
|
|
1463
|
-
}
|
|
1464
|
-
return (
|
|
1465
|
-
<div className="genericInputContainer">
|
|
1466
|
-
<Components.Text type="formLabel">Select User</Components.Text>
|
|
1467
|
-
{content}
|
|
1468
|
-
</div>
|
|
1469
|
-
);
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
renderUserSelectionPopup() {
|
|
1473
|
-
if (!this.state.showingAssigneeSelector) return null;
|
|
1474
|
-
return (
|
|
1475
|
-
<Components.Popup
|
|
1476
|
-
title="Assign Job"
|
|
1477
|
-
onClose={this.onCloseSelectAssignee}
|
|
1478
|
-
maxWidth={600}
|
|
1479
|
-
hasPadding
|
|
1480
|
-
buttons={[
|
|
1481
|
-
{
|
|
1482
|
-
type: "primary",
|
|
1483
|
-
onClick: this.onConfirmAssignee,
|
|
1484
|
-
isActive: !!this.state.selectedAssignee,
|
|
1485
|
-
text: "Confirm",
|
|
1486
|
-
},
|
|
1487
|
-
{
|
|
1488
|
-
type: "tertiary",
|
|
1489
|
-
onClick: this.onCloseSelectAssignee,
|
|
1490
|
-
isActive: true,
|
|
1491
|
-
text: "Cancel",
|
|
1492
|
-
},
|
|
1493
|
-
]}
|
|
1494
|
-
>
|
|
1495
|
-
{this.renderUsers()}
|
|
1496
|
-
</Components.Popup>
|
|
1497
|
-
);
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
render() {
|
|
1501
|
-
return (
|
|
1502
|
-
<Components.OverlayPage>
|
|
1503
|
-
{this.renderAddNotePopup()}
|
|
1504
|
-
{this.renderUserSelectionPopup()}
|
|
1505
|
-
<Components.OverlayPageContents>
|
|
1506
|
-
<Components.OverlayPageSection className="pageSectionWrapper--fixedPopupSize">
|
|
1507
|
-
{this.renderInner()}
|
|
1508
|
-
</Components.OverlayPageSection>
|
|
1509
|
-
<Components.OverlayPageSection className="pageSectionWrapper--newPopupSide pageSectionWrapper--newPopupSide-fixedWidth">
|
|
1510
|
-
{this.renderAssignment()}
|
|
1511
|
-
{this.renderPriority()}
|
|
1512
|
-
{this.renderExternalSync()}
|
|
1513
|
-
{this.renderOverview()}
|
|
1514
|
-
</Components.OverlayPageSection>
|
|
1515
|
-
</Components.OverlayPageContents>
|
|
1516
|
-
<Components.OverlayPageBottomButtons>
|
|
1517
|
-
{this.renderButtons()}
|
|
1518
|
-
</Components.OverlayPageBottomButtons>
|
|
1519
|
-
</Components.OverlayPage>
|
|
1520
|
-
);
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
const mapStateToProps = (state) => {
|
|
1525
|
-
const { auth } = state;
|
|
1526
|
-
return { auth, statusTypes: state[values.reducerKey].jobstatuses };
|
|
1527
|
-
};
|
|
1528
|
-
|
|
1529
|
-
export default connect(mapStateToProps, { jobsLoaded, jobStatusesUpdate })(
|
|
1530
|
-
withRouter(Job),
|
|
1531
|
-
);
|