| Line 394... |
Line 394... |
| 394 |
html += '<div class="beat-card beat-list-item" data-plangroup="' + b.planGroupId + '" style="cursor:pointer; position:relative;">';
|
394 |
html += '<div class="beat-card beat-list-item" data-plangroup="' + b.planGroupId + '" style="cursor:pointer; position:relative;">';
|
| 395 |
html += '<div class="beat-name"><span style="display:inline-block;width:12px;height:12px;border-radius:2px;background:' + b.beatColor + ';"></span> ' + b.beatName + ' <span class="status-badge ' + statusCls + '">' + statusLabel + '</span></div>';
|
395 |
html += '<div class="beat-name"><span style="display:inline-block;width:12px;height:12px;border-radius:2px;background:' + b.beatColor + ';"></span> ' + b.beatName + ' <span class="status-badge ' + statusCls + '">' + statusLabel + '</span></div>';
|
| 396 |
html += '<div class="beat-meta">' + b.days.length + ' days | ' + totalV + ' visits | ' + totalKm.toFixed(0) + ' km';
|
396 |
html += '<div class="beat-meta">' + b.days.length + ' days | ' + totalV + ' visits | ' + totalKm.toFixed(0) + ' km';
|
| 397 |
if (item.count > 1) html += ' | ' + item.count + 'x scheduled';
|
397 |
if (item.count > 1) html += ' | ' + item.count + 'x scheduled';
|
| 398 |
html += '</div>';
|
398 |
html += '</div>';
|
| - |
|
399 |
// Pass the beat's first scheduled date so date-specific leads load in edit mode.
|
| - |
|
400 |
var editDate = '';
|
| - |
|
401 |
(b.days || []).forEach(function (dd) { if (!editDate && dd.planDate) editDate = dd.planDate; });
|
| 399 |
html += '<button class="beat-edit-btn" data-plangroup="' + b.planGroupId + '" '
|
402 |
html += '<button class="beat-edit-btn" data-plangroup="' + b.planGroupId + '" data-date="' + editDate + '" '
|
| 400 |
+ 'style="position:absolute;top:8px;right:8px;background:#475569;color:#e2e8f0;'
|
403 |
+ 'style="position:absolute;top:8px;right:8px;background:#475569;color:#e2e8f0;'
|
| 401 |
+ 'border:none;border-radius:4px;padding:3px 8px;font-size:10px;cursor:pointer;'
|
404 |
+ 'border:none;border-radius:4px;padding:3px 8px;font-size:10px;cursor:pointer;'
|
| 402 |
+ 'font-family:inherit;">Edit</button>';
|
405 |
+ 'font-family:inherit;">Edit</button>';
|
| 403 |
html += '</div>';
|
406 |
html += '</div>';
|
| 404 |
});
|
407 |
});
|
| Line 1591... |
Line 1594... |
| 1591 |
if (state.savedPlanGroupId) {
|
1594 |
if (state.savedPlanGroupId) {
|
| 1592 |
alert('This beat is already saved.');
|
1595 |
alert('This beat is already saved.');
|
| 1593 |
showBeatList();
|
1596 |
showBeatList();
|
| 1594 |
return;
|
1597 |
return;
|
| 1595 |
}
|
1598 |
}
|
| 1596 |
if (state.days.every(function (d) {
|
1599 |
var allDaysEmpty = state.days.every(function (d) {
|
| 1597 |
return d.visits.length === 0;
|
1600 |
return d.visits.length === 0;
|
| 1598 |
})) {
|
1601 |
});
|
| - |
|
1602 |
if (allDaysEmpty) {
|
| - |
|
1603 |
if (state.mode === 'edit' && state.editingBeatId) {
|
| 1599 |
alert('No visits added');
|
1604 |
alert('No visits added');
|
| - |
|
1605 |
return;
|
| - |
|
1606 |
}
|
| - |
|
1607 |
// Empty beat = a container to assign leads into afterward (Assigned Leads → Schedule on a beat).
|
| - |
|
1608 |
if (!confirm('Save this beat with no visits?\n\nYou can schedule it on the calendar, then assign leads to it from "Assigned Leads → Schedule on a beat".')) {
|
| 1600 |
return;
|
1609 |
return;
|
| - |
|
1610 |
}
|
| 1601 |
}
|
1611 |
}
|
| 1602 |
|
1612 |
|
| 1603 |
var beatTitle = state.beatTitle || 'Beat';
|
1613 |
var beatTitle = state.beatTitle || 'Beat';
|
| 1604 |
var isEdit = state.mode === 'edit' && state.editingBeatId;
|
1614 |
var isEdit = state.mode === 'edit' && state.editingBeatId;
|
| 1605 |
|
1615 |
|
| Line 1673... |
Line 1683... |
| 1673 |
totalTimeMins: Math.round(dayMins),
|
1683 |
totalTimeMins: Math.round(dayMins),
|
| 1674 |
visits: visitsOut
|
1684 |
visits: visitsOut
|
| 1675 |
});
|
1685 |
});
|
| 1676 |
});
|
1686 |
});
|
| 1677 |
|
1687 |
|
| - |
|
1688 |
// Empty beat (no visits on any day): still emit day 1 as an empty container so
|
| - |
|
1689 |
// the beat + its schedule row are created. Leads are assigned to it afterward.
|
| - |
|
1690 |
if (daysData.length === 0) {
|
| - |
|
1691 |
var d0 = state.days[0] || {};
|
| - |
|
1692 |
var sLat = d0.startLat != null ? d0.startLat : state.homeLat;
|
| - |
|
1693 |
var sLng = d0.startLng != null ? d0.startLng : state.homeLng;
|
| - |
|
1694 |
daysData.push({
|
| - |
|
1695 |
dayNumber: 1,
|
| - |
|
1696 |
startLocationName: d0.startName || state.homeName,
|
| - |
|
1697 |
startLatitude: sLat != null ? String(sLat) : null,
|
| - |
|
1698 |
startLongitude: sLng != null ? String(sLng) : null,
|
| - |
|
1699 |
endAction: 'HOME',
|
| - |
|
1700 |
stayLocationName: null, stayLatitude: null, stayLongitude: null,
|
| - |
|
1701 |
totalDistanceKm: 0, totalTimeMins: 0,
|
| - |
|
1702 |
visits: []
|
| - |
|
1703 |
});
|
| - |
|
1704 |
}
|
| - |
|
1705 |
|
| 1678 |
// EDIT RULE: detect removed leads → show popup with cancel/reschedule per lead
|
1706 |
// EDIT RULE: detect removed leads → show popup with cancel/reschedule per lead
|
| 1679 |
var removedLeads = [];
|
1707 |
var removedLeads = [];
|
| 1680 |
if (isEdit && state.originalLeads && state.originalLeads.length) {
|
1708 |
if (isEdit && state.originalLeads && state.originalLeads.length) {
|
| 1681 |
var currentLeadIds = {};
|
1709 |
var currentLeadIds = {};
|
| 1682 |
state.days.forEach(function (d) {
|
1710 |
state.days.forEach(function (d) {
|