| Line 490... |
Line 490... |
| 490 |
&& Math.abs(parseFloat(l.longitude) - day1.startLng) < 1e-4;
|
490 |
&& Math.abs(parseFloat(l.longitude) - day1.startLng) < 1e-4;
|
| 491 |
});
|
491 |
});
|
| 492 |
if (match) state.activeBaseLocationId = match.id;
|
492 |
if (match) state.activeBaseLocationId = match.id;
|
| 493 |
}
|
493 |
}
|
| 494 |
|
494 |
|
| - |
|
495 |
// renderRoutePanel now builds the edit header (back btn, EDITING
|
| - |
|
496 |
// title, date label, base picker, partner search) inline — so we
|
| - |
|
497 |
// just call it without the one-time prepend that used to live here.
|
| 495 |
renderRoutePanel();
|
498 |
renderRoutePanel();
|
| 496 |
var dateLabel = planDate
|
- |
|
| 497 |
? '<div style="font-size:11px;color:#f59e0b;margin-bottom:8px;">Editing run on: ' + planDate + ' (leads on this date can be removed)</div>'
|
- |
|
| 498 |
: '<div style="font-size:11px;color:#94a3b8;margin-bottom:8px;">Editing beat template (partners only)</div>';
|
- |
|
| 499 |
$('#route-list').prepend(
|
- |
|
| 500 |
'<div style="margin-bottom:8px;"><button class="btn-back-to-list" '
|
- |
|
| 501 |
+ 'style="font-size:11px;padding:4px 10px;border:1px solid #475569;'
|
- |
|
| 502 |
+ 'border-radius:4px;background:none;color:#94a3b8;cursor:pointer;'
|
- |
|
| 503 |
+ 'font-family:inherit;">← Back to list</button></div>'
|
- |
|
| 504 |
+ '<div style="font-size:14px;font-weight:600;color:#f59e0b;margin-bottom:4px;">'
|
- |
|
| 505 |
+ 'EDITING: ' + state.beatTitle + '</div>' + dateLabel
|
- |
|
| 506 |
+ renderBaseLocationPicker()
|
- |
|
| 507 |
+ renderPartnerQuickAdd());
|
- |
|
| 508 |
|
499 |
|
| 509 |
initMap();
|
500 |
initMap();
|
| 510 |
// Add lead markers (orange) on the map
|
501 |
// Add lead markers (orange) on the map
|
| 511 |
state.days.forEach(function (day) {
|
502 |
state.days.forEach(function (day) {
|
| 512 |
day.visits.forEach(function (v) {
|
503 |
day.visits.forEach(function (v) {
|
| Line 1205... |
Line 1196... |
| 1205 |
if (state.mode === 'create' && state.beatTitle) {
|
1196 |
if (state.mode === 'create' && state.beatTitle) {
|
| 1206 |
html += '<div style="margin-bottom:8px;"><button class="btn-back-to-list" style="font-size:11px;padding:4px 10px;border:1px solid #475569;border-radius:4px;background:none;color:#94a3b8;cursor:pointer;font-family:inherit;">← Back</button></div>';
|
1197 |
html += '<div style="margin-bottom:8px;"><button class="btn-back-to-list" style="font-size:11px;padding:4px 10px;border:1px solid #475569;border-radius:4px;background:none;color:#94a3b8;cursor:pointer;font-family:inherit;">← Back</button></div>';
|
| 1207 |
html += '<div style="font-size:14px;font-weight:600;color:#60a5fa;margin-bottom:10px;">' + state.beatTitle + '</div>';
|
1198 |
html += '<div style="font-size:14px;font-weight:600;color:#60a5fa;margin-bottom:10px;">' + state.beatTitle + '</div>';
|
| 1208 |
html += renderBaseLocationPicker();
|
1199 |
html += renderBaseLocationPicker();
|
| 1209 |
html += renderPartnerQuickAdd();
|
1200 |
html += renderPartnerQuickAdd();
|
| - |
|
1201 |
} else if (state.mode === 'edit' && state.beatTitle) {
|
| - |
|
1202 |
// Edit header used to be injected via a one-time prepend() in editBeat's
|
| - |
|
1203 |
// success callback — that meant any addVisit-triggered re-render wiped the
|
| - |
|
1204 |
// back button / title / base picker / search box. Building it here keeps
|
| - |
|
1205 |
// them present across every re-render so users can keep adding partners.
|
| - |
|
1206 |
html += '<div style="margin-bottom:8px;"><button class="btn-back-to-list" style="font-size:11px;padding:4px 10px;border:1px solid #475569;border-radius:4px;background:none;color:#94a3b8;cursor:pointer;font-family:inherit;">← Back to list</button></div>';
|
| - |
|
1207 |
html += '<div style="font-size:14px;font-weight:600;color:#f59e0b;margin-bottom:4px;">EDITING: ' + state.beatTitle + '</div>';
|
| - |
|
1208 |
html += state.editingDate
|
| - |
|
1209 |
? '<div style="font-size:11px;color:#f59e0b;margin-bottom:8px;">Editing run on: ' + state.editingDate + ' (leads on this date can be removed)</div>'
|
| - |
|
1210 |
: '<div style="font-size:11px;color:#94a3b8;margin-bottom:8px;">Editing beat template (partners only)</div>';
|
| - |
|
1211 |
html += renderBaseLocationPicker();
|
| - |
|
1212 |
html += renderPartnerQuickAdd();
|
| 1210 |
}
|
1213 |
}
|
| 1211 |
state.days.forEach(function (day) {
|
1214 |
state.days.forEach(function (day) {
|
| 1212 |
var isCurrent = day.dayNumber === state.currentDay;
|
1215 |
var isCurrent = day.dayNumber === state.currentDay;
|
| 1213 |
var isLastDay = day.dayNumber === state.days.length;
|
1216 |
var isLastDay = day.dayNumber === state.days.length;
|
| 1214 |
html += '<div class="route-day">';
|
1217 |
html += '<div class="route-day">';
|
| Line 1835... |
Line 1838... |
| 1835 |
}
|
1838 |
}
|
| 1836 |
html += '</table>';
|
1839 |
html += '</table>';
|
| 1837 |
$('#cal-grid-content').html(html);
|
1840 |
$('#cal-grid-content').html(html);
|
| 1838 |
}
|
1841 |
}
|
| 1839 |
|
1842 |
|
| - |
|
1843 |
// Lists the scheduled dates for a beat (grouped by day number) inside a beat card.
|
| - |
|
1844 |
// Output looks like: "Schedule: D1 May 27 | D2 May 28 | D3 May 29"
|
| - |
|
1845 |
// Unscheduled days show as "—"; if NO real dates exist, returns empty string.
|
| - |
|
1846 |
function renderBeatScheduledDates(instances) {
|
| - |
|
1847 |
if (!instances || !instances.length) return '';
|
| - |
|
1848 |
|
| - |
|
1849 |
// Collect planDates per dayNumber across every instance of this beat
|
| - |
|
1850 |
var byDay = {};
|
| - |
|
1851 |
var today = fmtDate(new Date());
|
| - |
|
1852 |
instances.forEach(function (inst) {
|
| - |
|
1853 |
(inst.days || []).forEach(function (d) {
|
| - |
|
1854 |
if (!d.planDate) return; // skip unscheduled placeholders
|
| - |
|
1855 |
if (!byDay[d.dayNumber]) byDay[d.dayNumber] = [];
|
| - |
|
1856 |
byDay[d.dayNumber].push(d.planDate);
|
| - |
|
1857 |
});
|
| - |
|
1858 |
});
|
| - |
|
1859 |
|
| - |
|
1860 |
var dayNums = Object.keys(byDay).map(function (n) {
|
| - |
|
1861 |
return parseInt(n, 10);
|
| - |
|
1862 |
}).sort(function (a, b) {
|
| - |
|
1863 |
return a - b;
|
| - |
|
1864 |
});
|
| - |
|
1865 |
if (dayNums.length === 0) {
|
| - |
|
1866 |
return '<div class="beat-meta" style="color:#475569; margin-top:4px;">Not scheduled yet</div>';
|
| - |
|
1867 |
}
|
| - |
|
1868 |
|
| - |
|
1869 |
var MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
| - |
|
1870 |
|
| - |
|
1871 |
function fmt(ds) {
|
| - |
|
1872 |
// ds = "yyyy-mm-dd"
|
| - |
|
1873 |
var p = ds.split('-');
|
| - |
|
1874 |
if (p.length !== 3) return ds;
|
| - |
|
1875 |
return MONTHS[parseInt(p[1], 10) - 1] + ' ' + parseInt(p[2], 10);
|
| - |
|
1876 |
}
|
| - |
|
1877 |
|
| - |
|
1878 |
var parts = dayNums.map(function (n) {
|
| - |
|
1879 |
// De-dup + sort dates for this day (could repeat if beat scheduled multiple times)
|
| - |
|
1880 |
var dates = (byDay[n] || []).filter(function (v, i, a) {
|
| - |
|
1881 |
return a.indexOf(v) === i;
|
| - |
|
1882 |
}).sort();
|
| - |
|
1883 |
var label = dates.map(function (ds) {
|
| - |
|
1884 |
var color = ds === today ? '#ef4444' : (ds < today ? '#64748b' : '#60a5fa');
|
| - |
|
1885 |
return '<span style="color:' + color + ';">' + fmt(ds) + '</span>';
|
| - |
|
1886 |
}).join(', ');
|
| - |
|
1887 |
return '<span style="color:#94a3b8;">D' + n + '</span> ' + label;
|
| - |
|
1888 |
});
|
| - |
|
1889 |
|
| - |
|
1890 |
return '<div class="beat-meta" style="margin-top:4px; line-height:1.6;">'
|
| - |
|
1891 |
+ '<span style="color:#64748b;">Schedule:</span> '
|
| - |
|
1892 |
+ parts.join('<span style="color:#334155;"> | </span>')
|
| - |
|
1893 |
+ '</div>';
|
| - |
|
1894 |
}
|
| - |
|
1895 |
|
| 1840 |
function renderBeatCards(beats) {
|
1896 |
function renderBeatCards(beats) {
|
| 1841 |
var html = '';
|
1897 |
var html = '';
|
| 1842 |
if (!beats || beats.length === 0) {
|
1898 |
if (!beats || beats.length === 0) {
|
| 1843 |
html = '<p style="color:#64748b;font-size:12px;padding:20px;">No beats scheduled. Create a beat first.</p>';
|
1899 |
html = '<p style="color:#64748b;font-size:12px;padding:20px;">No beats scheduled. Create a beat first.</p>';
|
| 1844 |
$('#cal-beat-cards').html(html);
|
1900 |
$('#cal-beat-cards').html(html);
|
| Line 1891... |
Line 1947... |
| 1891 |
|
1947 |
|
| 1892 |
html += '<div class="beat-card" data-plangroup="' + primaryPg + '" data-beatname="' + (group.beatName || '') + '" draggable="true">';
|
1948 |
html += '<div class="beat-card" data-plangroup="' + primaryPg + '" data-beatname="' + (group.beatName || '') + '" draggable="true">';
|
| 1893 |
html += '<div class="beat-name"><span style="display:inline-block;width:12px;height:12px;border-radius:2px;background:' + group.beatColor + ';"></span> ' + group.beatName + '</div>';
|
1949 |
html += '<div class="beat-name"><span style="display:inline-block;width:12px;height:12px;border-radius:2px;background:' + group.beatColor + ';"></span> ' + group.beatName + '</div>';
|
| 1894 |
html += '<div class="beat-meta">' + first.days.length + ' days | ' + totalV + ' visits | ' + totalKm.toFixed(0) + ' km</div>';
|
1950 |
html += '<div class="beat-meta">' + first.days.length + ' days | ' + totalV + ' visits | ' + totalKm.toFixed(0) + ' km</div>';
|
| 1895 |
html += '<div class="beat-meta" style="color:#94a3b8;">' + statusText + ' (' + totalInstances + ' total)</div>';
|
1951 |
html += '<div class="beat-meta" style="color:#94a3b8;">' + statusText + ' (' + totalInstances + ' total)</div>';
|
| - |
|
1952 |
html += renderBeatScheduledDates(group.instances);
|
| 1896 |
|
1953 |
|
| 1897 |
html += '<div class="beat-actions">';
|
1954 |
html += '<div class="beat-actions">';
|
| 1898 |
if (isScheduling) {
|
1955 |
if (isScheduling) {
|
| 1899 |
html += '<button class="btn-sm btn-confirm-schedule" data-pg="' + primaryPg + '" data-days="' + first.days.length + '" style="background:#22c55e;color:#fff;">Confirm (' + state.calPickedDates.length + ' dates)</button>';
|
1956 |
html += '<button class="btn-sm btn-confirm-schedule" data-pg="' + primaryPg + '" data-days="' + first.days.length + '" style="background:#22c55e;color:#fff;">Confirm (' + state.calPickedDates.length + ' dates)</button>';
|
| 1900 |
html += '<button class="btn-sm btn-cancel-schedule" style="background:#475569;color:#e2e8f0;">Cancel</button>';
|
1957 |
html += '<button class="btn-sm btn-cancel-schedule" style="background:#475569;color:#e2e8f0;">Cancel</button>';
|