| Line 90... |
Line 90... |
| 90 |
function renderDayViewRows(rows) {
|
90 |
function renderDayViewRows(rows) {
|
| 91 |
if (!rows || rows.length === 0) {
|
91 |
if (!rows || rows.length === 0) {
|
| 92 |
$('#dayViewRows').html('<tr><td colspan="8" style="text-align:center; color:#999;">No beats scheduled in this range.</td></tr>');
|
92 |
$('#dayViewRows').html('<tr><td colspan="8" style="text-align:center; color:#999;">No beats scheduled in this range.</td></tr>');
|
| 93 |
return;
|
93 |
return;
|
| 94 |
}
|
94 |
}
|
| - |
|
95 |
// Assign Visit is only valid for TODAY's run — future/past dates hide the button.
|
| - |
|
96 |
var todayStr = (function () {
|
| - |
|
97 |
var d = new Date();
|
| - |
|
98 |
var mm = ('0' + (d.getMonth() + 1)).slice(-2);
|
| - |
|
99 |
var dd = ('0' + d.getDate()).slice(-2);
|
| - |
|
100 |
return d.getFullYear() + '-' + mm + '-' + dd;
|
| - |
|
101 |
})();
|
| 95 |
var html = '';
|
102 |
var html = '';
|
| 96 |
rows.forEach(function (r) {
|
103 |
rows.forEach(function (r) {
|
| - |
|
104 |
var isToday = r.scheduleDate === todayStr;
|
| 97 |
html += '<tr>'
|
105 |
html += '<tr>'
|
| 98 |
+ '<td>' + r.userName + '</td>'
|
106 |
+ '<td>' + r.userName + '</td>'
|
| 99 |
+ '<td><span class="label label-primary">' + r.scheduleDate + '</span></td>'
|
107 |
+ '<td><span class="label label-primary">' + r.scheduleDate + '</span></td>'
|
| 100 |
+ '<td><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:' + (r.beatColor || '#3498db') + ';margin-right:6px;"></span>' + r.beatName + '</td>'
|
108 |
+ '<td><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:' + (r.beatColor || '#3498db') + ';margin-right:6px;"></span>' + r.beatName + '</td>'
|
| 101 |
+ '<td>' + r.dayNumber + '</td>'
|
109 |
+ '<td>' + r.dayNumber + '</td>'
|
| Line 104... |
Line 112... |
| 104 |
+ '<td><strong>' + r.visitCount + '</strong></td>'
|
112 |
+ '<td><strong>' + r.visitCount + '</strong></td>'
|
| 105 |
+ '<td>'
|
113 |
+ '<td>'
|
| 106 |
+ '<button class="btn btn-xs btn-info view-user-calendar" data-userid="' + r.authUserId + '" data-username="' + r.userName + '">View Calendar</button> '
|
114 |
+ '<button class="btn btn-xs btn-info view-user-calendar" data-userid="' + r.authUserId + '" data-username="' + r.userName + '">View Calendar</button> '
|
| 107 |
+ '<button class="btn btn-xs btn-warning edit-beat-on-date" '
|
115 |
+ '<button class="btn btn-xs btn-warning edit-beat-on-date" '
|
| 108 |
+ 'data-userid="' + r.authUserId + '" data-username="' + r.userName + '" '
|
116 |
+ 'data-userid="' + r.authUserId + '" data-username="' + r.userName + '" '
|
| 109 |
+ 'data-beatid="' + r.beatId + '" data-date="' + r.scheduleDate + '">Edit</button> '
|
117 |
+ 'data-beatid="' + r.beatId + '" data-date="' + r.scheduleDate + '">Edit</button>'
|
| - |
|
118 |
+ (isToday
|
| 110 |
+ '<button class="btn btn-xs btn-success assign-visit-btn" '
|
119 |
? ' <button class="btn btn-xs btn-success assign-visit-btn"'
|
| 111 |
+ 'data-userid="' + r.authUserId + '" data-username="' + r.userName + '" '
|
120 |
+ ' data-userid="' + r.authUserId + '" data-username="' + r.userName + '"'
|
| - |
|
121 |
+ ' data-beatid="' + r.beatId + '"'
|
| 112 |
+ 'data-date="' + r.scheduleDate + '">Assign Visit</button>'
|
122 |
+ ' data-date="' + r.scheduleDate + '">Assign Visit</button>'
|
| - |
|
123 |
: '')
|
| 113 |
+ '</td>'
|
124 |
+ '</td>'
|
| 114 |
+ '</tr>';
|
125 |
+ '</tr>';
|
| 115 |
});
|
126 |
});
|
| 116 |
$('#dayViewRows').html(html);
|
127 |
$('#dayViewRows').html(html);
|
| 117 |
}
|
128 |
}
|
| Line 140... |
Line 151... |
| 140 |
$('#userCalendarFrame').attr('src', url);
|
151 |
$('#userCalendarFrame').attr('src', url);
|
| 141 |
$('#userCalendarModal').modal('show');
|
152 |
$('#userCalendarModal').modal('show');
|
| 142 |
});
|
153 |
});
|
| 143 |
|
154 |
|
| 144 |
// ---------- ASSIGN VISIT ----------
|
155 |
// ---------- ASSIGN VISIT ----------
|
| 145 |
var avContext = {authUserId: null, userName: '', date: '', allParties: []};
|
156 |
var avContext = {authUserId: null, userName: '', date: '', beatId: null, allParties: [], agendaOptions: []};
|
| 146 |
|
157 |
|
| 147 |
$(document).on('click', '.assign-visit-btn', function () {
|
158 |
$(document).on('click', '.assign-visit-btn', function () {
|
| 148 |
avContext.authUserId = $(this).data('userid');
|
159 |
avContext.authUserId = $(this).data('userid');
|
| 149 |
avContext.userName = $(this).data('username') || '';
|
160 |
avContext.userName = $(this).data('username') || '';
|
| 150 |
avContext.date = $(this).data('date');
|
161 |
avContext.date = $(this).data('date');
|
| - |
|
162 |
avContext.beatId = $(this).data('beatid');
|
| 151 |
avContext.allParties = [];
|
163 |
avContext.allParties = [];
|
| 152 |
|
164 |
|
| 153 |
$('#avTitle').text('Assign Visit — ' + avContext.userName + ' on ' + avContext.date);
|
165 |
$('#avTitle').text('Assign Visit - ' + avContext.userName + ' on ' + avContext.date);
|
| 154 |
$('#avSearch').val('');
|
166 |
$('#avSearch').val('');
|
| - |
|
167 |
// The default-agenda multiselect is rebuilt (and re-initialized) below in rebuildDefaultAgenda
|
| 155 |
$('#avPartyList').html('<div style="padding:20px; color:#999; text-align:center;">Loading parties...</div>');
|
168 |
$('#avPartyList').html('<div style="padding:20px; color:#999; text-align:center;">Loading parties...</div>');
|
| 156 |
$('#avMsg').text('');
|
169 |
$('#avMsg').text('');
|
| 157 |
$('#avSelectedCount').text('0 selected');
|
170 |
$('#avSelectedCount').text('0 selected');
|
| 158 |
$('#avSubmit').prop('disabled', true);
|
171 |
$('#avSubmit').prop('disabled', true);
|
| 159 |
$('#assignVisitModal').modal('show');
|
172 |
$('#assignVisitModal').modal('show');
|
| 160 |
|
173 |
|
| 161 |
$.get(context + '/beatPlan/assignVisit/parties', {authUserId: avContext.authUserId})
|
174 |
$.get(context + '/beatPlan/assignVisit/parties', {
|
| - |
|
175 |
authUserId: avContext.authUserId,
|
| - |
|
176 |
date: avContext.date,
|
| - |
|
177 |
beatId: avContext.beatId
|
| 162 |
.done(function (r) {
|
178 |
}).done(function (r) {
|
| 163 |
var data = r.response || r;
|
179 |
var data = r.response || r;
|
| - |
|
180 |
// Drop parties already in the beat — they're handled by the beat plan itself,
|
| - |
|
181 |
// Assign Visit is purely for adding extra (non-beat) visits.
|
| - |
|
182 |
avContext.allParties = (data.parties || []).filter(function (p) {
|
| - |
|
183 |
return !p.inBeat;
|
| - |
|
184 |
});
|
| 164 |
avContext.allParties = data.parties || [];
|
185 |
avContext.agendaOptions = data.agendaOptions || [];
|
| 165 |
if (!data.dtrUserId) {
|
186 |
if (!data.dtrUserId) {
|
| 166 |
$('#avPartyList').html('<div style="padding:20px; color:#c62828; text-align:center;">' +
|
187 |
$('#avPartyList').html('<div style="padding:20px; color:#c62828; text-align:center;">' +
|
| 167 |
'This user has no dtr.users record (cannot create visit tasks).</div>');
|
188 |
'This user has no dtr.users record (cannot create visit tasks).</div>');
|
| 168 |
return;
|
189 |
return;
|
| 169 |
}
|
190 |
}
|
| - |
|
191 |
rebuildDefaultAgenda();
|
| 170 |
renderAvParties(avContext.allParties);
|
192 |
renderAvParties(avContext.allParties);
|
| 171 |
})
|
- |
|
| 172 |
.fail(function (xhr) {
|
193 |
}).fail(function (xhr) {
|
| 173 |
$('#avPartyList').html('<div style="padding:20px; color:#c62828; text-align:center;">' +
|
194 |
$('#avPartyList').html('<div style="padding:20px; color:#c62828; text-align:center;">' +
|
| 174 |
'Error loading parties: ' + (xhr.responseText || xhr.statusText) + '</div>');
|
195 |
'Error loading parties: ' + (xhr.responseText || xhr.statusText) + '</div>');
|
| 175 |
});
|
196 |
});
|
| 176 |
});
|
197 |
});
|
| 177 |
|
198 |
|
| - |
|
199 |
// Per-row agenda markup. Initialized as bootstrap-multiselect dropdown
|
| - |
|
200 |
// (project standard — same as today-offer.vm, lead.vm, etc.) after render.
|
| - |
|
201 |
// In-beat parties are filtered out of the list entirely, so only non-beat
|
| - |
|
202 |
// (extra-visit) parties show up here. existingAgendas[] is pre-selected for
|
| - |
|
203 |
// parties that already have a saved visit today (so editing existing ones works).
|
| - |
|
204 |
function agendaSelectHtml(p) {
|
| - |
|
205 |
var preset = {};
|
| - |
|
206 |
(p.existingAgendas || []).forEach(function (a) {
|
| - |
|
207 |
preset[a] = true;
|
| - |
|
208 |
});
|
| - |
|
209 |
var html = '<select multiple class="av-party-agenda"'
|
| - |
|
210 |
+ ' data-fofoid="' + p.fofoStoreId + '">';
|
| - |
|
211 |
avContext.agendaOptions.forEach(function (opt) {
|
| - |
|
212 |
html += '<option value="' + opt + '"' + (preset[opt] ? ' selected' : '') + '>' + opt + '</option>';
|
| - |
|
213 |
});
|
| - |
|
214 |
html += '</select>';
|
| - |
|
215 |
return html;
|
| - |
|
216 |
}
|
| - |
|
217 |
|
| - |
|
218 |
var AGENDA_MULTISELECT_OPTS = {
|
| - |
|
219 |
includeSelectAllOption: false,
|
| - |
|
220 |
maxHeight: 220,
|
| - |
|
221 |
buttonWidth: '240px',
|
| - |
|
222 |
numberDisplayed: 2,
|
| - |
|
223 |
nonSelectedText: 'Pick agenda(s)',
|
| - |
|
224 |
nSelectedText: ' agendas',
|
| - |
|
225 |
allSelectedText: 'All agendas',
|
| - |
|
226 |
enableFiltering: true,
|
| - |
|
227 |
enableCaseInsensitiveFiltering: true
|
| - |
|
228 |
};
|
| - |
|
229 |
|
| - |
|
230 |
function rebuildDefaultAgenda() {
|
| - |
|
231 |
var $sel = $('#avDefaultAgenda');
|
| - |
|
232 |
// Destroy any prior instance before re-initing (re-open of the modal)
|
| - |
|
233 |
try {
|
| - |
|
234 |
$sel.multiselect('destroy');
|
| - |
|
235 |
} catch (e) {
|
| - |
|
236 |
}
|
| - |
|
237 |
var html = '';
|
| - |
|
238 |
avContext.agendaOptions.forEach(function (opt) {
|
| - |
|
239 |
html += '<option value="' + opt + '">' + opt + '</option>';
|
| - |
|
240 |
});
|
| - |
|
241 |
$sel.html(html);
|
| - |
|
242 |
// Build opts as a variable — inline {...} object literal after $.extend()
|
| - |
|
243 |
// confused Velocity's parser ("Encountered { ... was expecting ...").
|
| - |
|
244 |
var defaultOpts = $.extend({}, AGENDA_MULTISELECT_OPTS);
|
| - |
|
245 |
defaultOpts.nonSelectedText = 'Pick agenda(s) to apply';
|
| - |
|
246 |
$sel.multiselect(defaultOpts);
|
| - |
|
247 |
}
|
| - |
|
248 |
|
| - |
|
249 |
function initRowMultiselects() {
|
| - |
|
250 |
$('.av-party-agenda').each(function () {
|
| - |
|
251 |
var $sel = $(this);
|
| - |
|
252 |
try {
|
| - |
|
253 |
$sel.multiselect('destroy');
|
| - |
|
254 |
} catch (e) {
|
| - |
|
255 |
}
|
| - |
|
256 |
$sel.multiselect(AGENDA_MULTISELECT_OPTS);
|
| - |
|
257 |
});
|
| - |
|
258 |
}
|
| - |
|
259 |
|
| 178 |
function renderAvParties(list) {
|
260 |
function renderAvParties(list) {
|
| 179 |
if (!list || list.length === 0) {
|
261 |
if (!list || list.length === 0) {
|
| 180 |
$('#avPartyList').html('<div style="padding:20px; color:#999; text-align:center;">No parties found.</div>');
|
262 |
$('#avPartyList').html('<div style="padding:20px; color:#999; text-align:center;">No parties found.</div>');
|
| 181 |
return;
|
263 |
return;
|
| 182 |
}
|
264 |
}
|
| 183 |
var html = '<table class="table table-condensed table-hover" style="margin-bottom:0; font-size:13px;">';
|
265 |
var html = '<table class="table table-condensed table-hover" style="margin-bottom:0; font-size:13px;">';
|
| 184 |
html += '<thead><tr>'
|
266 |
html += '<thead><tr>'
|
| 185 |
+ '<th style="width:30px;"><input type="checkbox" id="avSelectAll" title="Select all"></th>'
|
267 |
+ '<th style="width:30px;"><input type="checkbox" id="avSelectAll" title="Select all"></th>'
|
| 186 |
+ '<th>Code</th><th>Outlet</th><th>City</th>'
|
268 |
+ '<th>Code</th><th>Outlet</th><th>City</th>'
|
| 187 |
+ '<th style="width:220px;">Agenda</th>'
|
269 |
+ '<th style="width:260px;">Agenda</th>'
|
| - |
|
270 |
+ '<th style="width:260px;">Description</th>'
|
| 188 |
+ '</tr></thead><tbody>';
|
271 |
+ '</tr></thead><tbody>';
|
| 189 |
list.forEach(function (p) {
|
272 |
list.forEach(function (p) {
|
| 190 |
var safeName = (p.outletName || '').replace(/"/g, '"');
|
273 |
var safeName = (p.outletName || '').replace(/"/g, '"');
|
| - |
|
274 |
var existingDesc = (p.existingDescription || '').replace(/"/g, '"');
|
| 191 |
html += '<tr>'
|
275 |
html += '<tr>'
|
| 192 |
+ '<td><input type="checkbox" class="av-party-chk" '
|
276 |
+ '<td><input type="checkbox" class="av-party-chk" '
|
| 193 |
+ 'data-fofoid="' + p.fofoStoreId + '" '
|
277 |
+ 'data-fofoid="' + p.fofoStoreId + '" '
|
| 194 |
+ 'data-name="' + safeName + '" '
|
278 |
+ 'data-name="' + safeName + '" '
|
| 195 |
+ 'data-lat="' + (p.latitude || '') + '" '
|
279 |
+ 'data-lat="' + (p.latitude || '') + '" '
|
| 196 |
+ 'data-lng="' + (p.longitude || '') + '"></td>'
|
280 |
+ 'data-lng="' + (p.longitude || '') + '"></td>'
|
| 197 |
+ '<td>' + (p.code || '') + '</td>'
|
281 |
+ '<td>' + (p.code || '') + '</td>'
|
| 198 |
+ '<td>' + (p.outletName || '') + '</td>'
|
282 |
+ '<td>' + (p.outletName || '') + '</td>'
|
| 199 |
+ '<td>' + (p.city || '') + '</td>'
|
283 |
+ '<td>' + (p.city || '') + '</td>'
|
| - |
|
284 |
+ '<td>' + agendaSelectHtml(p) + '</td>'
|
| 200 |
+ '<td><input type="text" class="form-control input-sm av-party-agenda" '
|
285 |
+ '<td><textarea class="form-control input-sm av-party-desc"'
|
| 201 |
+ 'placeholder="e.g. Stock review" '
|
286 |
+ ' data-fofoid="' + p.fofoStoreId + '"'
|
| 202 |
+ 'data-fofoid="' + p.fofoStoreId + '"></td>'
|
287 |
+ ' rows="2" placeholder="Optional notes..."'
|
| - |
|
288 |
+ ' style="resize:vertical; min-height:34px;">' + existingDesc + '</textarea></td>'
|
| 203 |
+ '</tr>';
|
289 |
+ '</tr>';
|
| 204 |
});
|
290 |
});
|
| 205 |
html += '</tbody></table>';
|
291 |
html += '</tbody></table>';
|
| 206 |
$('#avPartyList').html(html);
|
292 |
$('#avPartyList').html(html);
|
| - |
|
293 |
initRowMultiselects();
|
| 207 |
updateAvSelectedCount();
|
294 |
updateAvSelectedCount();
|
| 208 |
}
|
295 |
}
|
| 209 |
|
296 |
|
| 210 |
// Search filter
|
297 |
// Search filter
|
| 211 |
$(document).on('input', '#avSearch', function () {
|
298 |
$(document).on('input', '#avSearch', function () {
|
| Line 226... |
Line 313... |
| 226 |
$(document).on('change', '#avSelectAll', function () {
|
313 |
$(document).on('change', '#avSelectAll', function () {
|
| 227 |
$('.av-party-chk').prop('checked', this.checked);
|
314 |
$('.av-party-chk').prop('checked', this.checked);
|
| 228 |
updateAvSelectedCount();
|
315 |
updateAvSelectedCount();
|
| 229 |
});
|
316 |
});
|
| 230 |
|
317 |
|
| 231 |
// Per-row checkbox
|
- |
|
| 232 |
$(document).on('change', '.av-party-chk', updateAvSelectedCount);
|
318 |
$(document).on('change', '.av-party-chk', updateAvSelectedCount);
|
| 233 |
|
319 |
|
| 234 |
function updateAvSelectedCount() {
|
320 |
function updateAvSelectedCount() {
|
| 235 |
var n = $('.av-party-chk:checked').length;
|
321 |
var n = $('.av-party-chk:checked').length;
|
| 236 |
$('#avSelectedCount').text(n + ' selected');
|
322 |
$('#avSelectedCount').text(n + ' selected');
|
| 237 |
$('#avSubmit').prop('disabled', n === 0);
|
323 |
$('#avSubmit').prop('disabled', n === 0);
|
| 238 |
}
|
324 |
}
|
| 239 |
|
325 |
|
| 240 |
// Apply the default agenda value to all currently-selected rows
|
326 |
// Apply the default agendas (multi-select) to all currently-selected rows.
|
| - |
|
327 |
// MERGES with each row's existing selections so in-beat rows keep prior agendas.
|
| - |
|
328 |
// After updating <select>.val(), call multiselect('refresh') so the checkbox
|
| - |
|
329 |
// UI re-syncs with the underlying option state.
|
| 241 |
$(document).on('click', '#avApplyAgenda', function () {
|
330 |
$(document).on('click', '#avApplyAgenda', function () {
|
| 242 |
var def = $('#avDefaultAgenda').val();
|
331 |
var defValues = $('#avDefaultAgenda').val() || [];
|
| 243 |
if (!def) {
|
332 |
if (defValues.length === 0) {
|
| 244 |
alert('Type an agenda first');
|
333 |
alert('Pick at least one agenda above first');
|
| 245 |
return;
|
334 |
return;
|
| 246 |
}
|
335 |
}
|
| 247 |
$('.av-party-chk:checked').each(function () {
|
336 |
$('.av-party-chk:checked').each(function () {
|
| 248 |
var fid = $(this).data('fofoid');
|
337 |
var fid = $(this).data('fofoid');
|
| 249 |
$('.av-party-agenda[data-fofoid="' + fid + '"]').val(def);
|
338 |
var $sel = $('.av-party-agenda[data-fofoid="' + fid + '"]');
|
| - |
|
339 |
var existing = $sel.val() || [];
|
| - |
|
340 |
var merged = existing.slice();
|
| - |
|
341 |
defValues.forEach(function (v) {
|
| - |
|
342 |
if (merged.indexOf(v) === -1) merged.push(v);
|
| - |
|
343 |
});
|
| - |
|
344 |
$sel.val(merged);
|
| - |
|
345 |
try {
|
| - |
|
346 |
$sel.multiselect('refresh');
|
| - |
|
347 |
} catch (e) {
|
| - |
|
348 |
}
|
| 250 |
});
|
349 |
});
|
| 251 |
});
|
350 |
});
|
| 252 |
|
351 |
|
| 253 |
// Submit
|
352 |
// Submit
|
| 254 |
$(document).on('click', '#avSubmit', function () {
|
353 |
$(document).on('click', '#avSubmit', function () {
|
| 255 |
var picks = [];
|
354 |
var picks = [];
|
| 256 |
var missing = 0;
|
355 |
var missing = 0;
|
| 257 |
$('.av-party-chk:checked').each(function () {
|
356 |
$('.av-party-chk:checked').each(function () {
|
| 258 |
var fid = parseInt($(this).data('fofoid'));
|
357 |
var fid = parseInt($(this).data('fofoid'));
|
| 259 |
var agenda = $('.av-party-agenda[data-fofoid="' + fid + '"]').val();
|
358 |
var agendas = $('.av-party-agenda[data-fofoid="' + fid + '"]').val() || [];
|
| 260 |
agenda = agenda ? agenda.trim() : '';
|
359 |
var description = ($('.av-party-desc[data-fofoid="' + fid + '"]').val() || '').trim();
|
| 261 |
if (!agenda) missing++;
|
360 |
if (agendas.length === 0) missing++;
|
| 262 |
picks.push({
|
361 |
picks.push({
|
| 263 |
fofoStoreId: fid,
|
362 |
fofoStoreId: fid,
|
| 264 |
outletName: $(this).data('name'),
|
363 |
outletName: $(this).data('name'),
|
| 265 |
latitude: $(this).data('lat') ? String($(this).data('lat')) : null,
|
364 |
latitude: $(this).data('lat') ? String($(this).data('lat')) : null,
|
| 266 |
longitude: $(this).data('lng') ? String($(this).data('lng')) : null,
|
365 |
longitude: $(this).data('lng') ? String($(this).data('lng')) : null,
|
| 267 |
agenda: agenda
|
366 |
agendas: agendas,
|
| - |
|
367 |
description: description
|
| 268 |
});
|
368 |
});
|
| 269 |
});
|
369 |
});
|
| 270 |
if (picks.length === 0) return;
|
370 |
if (picks.length === 0) return;
|
| 271 |
if (missing > 0) {
|
371 |
if (missing > 0) {
|
| 272 |
if (!confirm(missing + ' selected row(s) have no agenda. Submit anyway with default "Visit"?')) return;
|
372 |
if (!confirm(missing + ' selected row(s) have no agenda. Submit anyway with default "Visit"?')) return;
|
| Line 285... |
Line 385... |
| 285 |
planDate: avContext.date,
|
385 |
planDate: avContext.date,
|
| 286 |
parties: picks
|
386 |
parties: picks
|
| 287 |
}),
|
387 |
}),
|
| 288 |
success: function (r) {
|
388 |
success: function (r) {
|
| 289 |
var d = r.response || r;
|
389 |
var d = r.response || r;
|
| 290 |
$('#avMsg').html('<span style="color:#2e7d32;">' + (d.message || 'Assigned successfully') + '</span>');
|
390 |
$('#avMsg').html('<span style="color:#2e7d32;">' + (d.message || 'Saved') + '</span>');
|
| 291 |
btn.prop('disabled', false).text('Assign Selected');
|
391 |
btn.prop('disabled', false).text('Save Assignments');
|
| 292 |
setTimeout(function () {
|
392 |
setTimeout(function () {
|
| 293 |
$('#assignVisitModal').modal('hide');
|
393 |
$('#assignVisitModal').modal('hide');
|
| 294 |
}, 1200);
|
394 |
}, 1500);
|
| 295 |
},
|
395 |
},
|
| 296 |
error: function (xhr) {
|
396 |
error: function (xhr) {
|
| 297 |
var msg = 'Failed';
|
397 |
var msg = 'Failed';
|
| 298 |
try {
|
398 |
try {
|
| 299 |
msg = (JSON.parse(xhr.responseText).response.message) || msg;
|
399 |
msg = (JSON.parse(xhr.responseText).response.message) || msg;
|
| 300 |
} catch (e) {
|
400 |
} catch (e) {
|
| 301 |
}
|
401 |
}
|
| 302 |
$('#avMsg').html('<span style="color:#c62828;">' + msg + '</span>');
|
402 |
$('#avMsg').html('<span style="color:#c62828;">' + msg + '</span>');
|
| 303 |
btn.prop('disabled', false).text('Assign Selected');
|
403 |
btn.prop('disabled', false).text('Save Assignments');
|
| 304 |
}
|
404 |
}
|
| 305 |
});
|
405 |
});
|
| 306 |
});
|
406 |
});
|
| 307 |
</script>
|
407 |
</script>
|
| 308 |
|
408 |
|
| Line 313... |
Line 413... |
| 313 |
<div class="modal-header">
|
413 |
<div class="modal-header">
|
| 314 |
<button type="button" class="close" data-dismiss="modal">×</button>
|
414 |
<button type="button" class="close" data-dismiss="modal">×</button>
|
| 315 |
<h4 class="modal-title" id="avTitle">Assign Visit</h4>
|
415 |
<h4 class="modal-title" id="avTitle">Assign Visit</h4>
|
| 316 |
</div>
|
416 |
</div>
|
| 317 |
<div class="modal-body">
|
417 |
<div class="modal-body">
|
| 318 |
<div style="display:flex; gap:10px; align-items:center; margin-bottom:10px;">
|
418 |
<div style="display:flex; gap:10px; align-items:flex-start; margin-bottom:10px;">
|
| - |
|
419 |
<div style="flex:1;">
|
| 319 |
<input type="text" id="avDefaultAgenda" class="form-control input-sm"
|
420 |
<label style="font-size:11px; color:#666; font-weight:normal; margin-bottom:2px;">
|
| 320 |
placeholder="Agenda (applies to selected rows)" style="flex:1;">
|
421 |
Agenda - applies to selected rows
|
| - |
|
422 |
</label>
|
| - |
|
423 |
<select id="avDefaultAgenda" multiple></select>
|
| - |
|
424 |
</div>
|
| 321 |
<button type="button" class="btn btn-default btn-sm" id="avApplyAgenda"
|
425 |
<button type="button" class="btn btn-default btn-sm" id="avApplyAgenda"
|
| - |
|
426 |
style="margin-top:18px;"
|
| 322 |
title="Fill agenda into all selected rows">
|
427 |
title="Add the picked agendas into all selected rows (merges with existing)">
|
| 323 |
Apply to Selected
|
428 |
Apply to Selected
|
| 324 |
</button>
|
429 |
</button>
|
| 325 |
</div>
|
430 |
</div>
|
| 326 |
<div style="display:flex; gap:10px; align-items:center; margin-bottom:10px;">
|
431 |
<div style="display:flex; gap:10px; align-items:center; margin-bottom:10px;">
|
| 327 |
<input type="text" id="avSearch" class="form-control input-sm"
|
432 |
<input type="text" id="avSearch" class="form-control input-sm"
|
| Line 332... |
Line 437... |
| 332 |
style="max-height:50vh; overflow-y:auto; border:1px solid #e5e5e5; border-radius:4px;"></div>
|
437 |
style="max-height:50vh; overflow-y:auto; border:1px solid #e5e5e5; border-radius:4px;"></div>
|
| 333 |
<div id="avMsg" style="margin-top:10px; font-size:12px;"></div>
|
438 |
<div id="avMsg" style="margin-top:10px; font-size:12px;"></div>
|
| 334 |
</div>
|
439 |
</div>
|
| 335 |
<div class="modal-footer">
|
440 |
<div class="modal-footer">
|
| 336 |
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
441 |
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
| 337 |
<button type="button" class="btn btn-success" id="avSubmit" disabled>Assign Selected</button>
|
442 |
<button type="button" class="btn btn-success" id="avSubmit" disabled>Save Assignments</button>
|
| 338 |
</div>
|
443 |
</div>
|
| 339 |
</div>
|
444 |
</div>
|
| 340 |
</div>
|
445 |
</div>
|
| 341 |
</div>
|
446 |
</div>
|
| 342 |
|
447 |
|