Subversion Repositories SmartDukaan

Rev

Rev 36785 | Rev 36788 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 36785 Rev 36787
Line 71... Line 71...
71
	state.homeName = '';
71
	state.homeName = '';
72
	state.activeBaseLocationId = null;
72
	state.activeBaseLocationId = null;
73
	state.baseLocations = [];
73
	state.baseLocations = [];
74
	state.viewDate = null;
74
	state.viewDate = null;
75
	state.originalDayCount = 0;
75
	state.originalDayCount = 0;
-
 
76
	state.visitRequests = [];
-
 
77
	state.assignVisitRequest = null;
-
 
78
	$('#visit-request-panel').hide();
76
	// Clear the rendered calendar grid + route map / panel so nothing from
79
	// Clear the rendered calendar grid + route map / panel so nothing from
77
	// the previous user is briefly visible while the new fetch is in flight.
80
	// the previous user is briefly visible while the new fetch is in flight.
78
	$('#cal-grid-content').empty();
81
	$('#cal-grid-content').empty();
79
	$('#route-list').empty();
82
	$('#route-list').empty();
80
	if (typeof clearRoute === 'function') {
83
	if (typeof clearRoute === 'function') {
Line 129... Line 132...
129
			if ($('.panel-tab[data-tab="calendar"]').hasClass('active')) {
132
			if ($('.panel-tab[data-tab="calendar"]').hasClass('active')) {
130
				var now = new Date();
133
				var now = new Date();
131
				state.calMonth = now.getFullYear() + '-' + ('0' + (now.getMonth() + 1)).slice(-2);
134
				state.calMonth = now.getFullYear() + '-' + ('0' + (now.getMonth() + 1)).slice(-2);
132
				loadCalendarData();
135
				loadCalendarData();
133
			}
136
			}
-
 
137
			// Pending visit requests for this user's hierarchy. Hidden when empty.
-
 
138
			if (typeof loadVisitRequests === 'function') {
-
 
139
				loadVisitRequests();
-
 
140
			}
134
        }
141
        }
135
    });
142
    });
136
});
143
});
137
 
144
 
138
// Called from the picker → swap the start location for the beat being
145
// Called from the picker → swap the start location for the beat being
Line 1823... Line 1830...
1823
	if (state.assignDeferred) {
1830
	if (state.assignDeferred) {
1824
		assignDeferredToBeat(String(pg), String(date));
1831
		assignDeferredToBeat(String(pg), String(date));
1825
		return;
1832
		return;
1826
	}
1833
	}
1827
 
1834
 
-
 
1835
	// Visit-request scheduling mode: similar handoff but talks to /visitRequest/{id}/approve-schedule.
-
 
1836
	if (state.assignVisitRequest) {
-
 
1837
		approveVisitRequestOnBeat(String(pg), String(date));
-
 
1838
		return;
-
 
1839
	}
-
 
1840
 
1828
	// Switch to Route tab and load that day's run
1841
	// Switch to Route tab and load that day's run
1829
	$('.panel-tab[data-tab="route"]').click();
1842
	$('.panel-tab[data-tab="route"]').click();
1830
	viewBeat(String(pg), String(date));
1843
	viewBeat(String(pg), String(date));
1831
});
1844
});
1832
 
1845
 
Line 2651... Line 2664...
2651
		error: function (xhr) {
2664
		error: function (xhr) {
2652
			console.error('[BEAT-AUTO] getBaseLocation failed:', xhr.status, xhr.responseText);
2665
			console.error('[BEAT-AUTO] getBaseLocation failed:', xhr.status, xhr.responseText);
2653
		}
2666
		}
2654
	});
2667
	});
2655
});
2668
});
-
 
2669
 
-
 
2670
// ============ VISIT REQUEST PANEL ============
-
 
2671
// Lives on the route tab (sidebar) when a sales user is loaded. Fetches
-
 
2672
// PENDING requests addressed at this user's hierarchy and shows three actions
-
 
2673
// per row: Schedule on a beat, Reassign, Reject. Schedule reuses the calendar
-
 
2674
// chip-click flow by setting state.assignVisitRequest = {…} and switching
-
 
2675
// the panel to the Calendar tab — clicking a future chip then fires the POST.
-
 
2676
function loadVisitRequests() {
-
 
2677
	if (!state.authUserId) return;
-
 
2678
	$.ajax({
-
 
2679
		url: context + '/visitRequest/list',
-
 
2680
		type: 'GET', dataType: 'json',
-
 
2681
		data: {assigneeAuthId: state.authUserId, status: 'PENDING'},
-
 
2682
		success: function (r) {
-
 
2683
			var data = r.response || r;
-
 
2684
			var rows = (data && data.rows) || [];
-
 
2685
			state.visitRequests = rows;
-
 
2686
			renderVisitRequests(rows);
-
 
2687
		},
-
 
2688
		error: function () {
-
 
2689
			$('#visit-request-panel').hide();
-
 
2690
		}
-
 
2691
	});
-
 
2692
}
-
 
2693
 
-
 
2694
function renderVisitRequests(rows) {
-
 
2695
	if (!rows || rows.length === 0) {
-
 
2696
		$('#visit-request-panel').hide();
-
 
2697
		return;
-
 
2698
	}
-
 
2699
	$('#vr-count').text('(' + rows.length + ')');
-
 
2700
	var html = '';
-
 
2701
	rows.forEach(function (r) {
-
 
2702
		var leadLine = '<strong style="color:#e2e8f0;">' + (r.leadName || ('Lead #' + r.leadId)) + '</strong>';
-
 
2703
		if (r.leadOutlet) leadLine += ' · <span style="color:#cbd5e1;">' + r.leadOutlet + '</span>';
-
 
2704
		if (r.leadMobile) leadLine += ' · <span style="color:#94a3b8;">' + r.leadMobile + '</span>';
-
 
2705
		var nearLine = '';
-
 
2706
		if (r.nearestStoreCode) nearLine = '<div style="color:#94a3b8;font-size:11px;">Near: ' + r.nearestStoreCode
-
 
2707
			+ (r.nearestStoreOutlet ? ' — ' + r.nearestStoreOutlet : '') + '</div>';
-
 
2708
		// Show "Name (#authUserId)" so the head can quickly match identities —
-
 
2709
		// especially useful when they need the id for the Reassign prompt.
-
 
2710
		var reqByLabel = (r.requestedByName || '-') + (r.requestedByAuthId ? ' (#' + r.requestedByAuthId + ')' : '');
-
 
2711
		var assigneeLabel = (r.assigneeName || '-') + (r.assigneeAuthId ? ' (#' + r.assigneeAuthId + ')' : '');
-
 
2712
		var reqMeta = '<div style="color:#94a3b8;font-size:11px;">'
-
 
2713
			+ 'Assignee: <span style="color:#cbd5e1;">' + assigneeLabel + '</span>'
-
 
2714
			+ ' · Requested by ' + reqByLabel
-
 
2715
			+ (r.requestedDate ? ' · Preferred: <strong style="color:#fbbf24;">' + r.requestedDate + '</strong>' : '')
-
 
2716
			+ (r.communicationType ? ' · ' + r.communicationType : '')
-
 
2717
			+ '</div>';
-
 
2718
		var safeName = String(r.leadName || '').replace(/"/g, '&quot;');
-
 
2719
		html += '<div data-rid="' + r.id + '" style="padding:8px;border:1px solid #334155;border-radius:6px;background:#0f172a;margin-bottom:6px;">'
-
 
2720
			+ leadLine
-
 
2721
			+ nearLine
-
 
2722
			+ reqMeta
-
 
2723
			+ '<div style="margin-top:6px;display:flex;gap:6px;flex-wrap:wrap;">'
-
 
2724
			+ '<button class="vr-schedule" data-rid="' + r.id + '" data-lead="' + r.leadId + '" data-leadname="' + safeName + '" data-assignee="' + r.assigneeAuthId + '" style="font-size:11px;padding:3px 8px;border:none;border-radius:4px;background:#22c55e;color:#fff;cursor:pointer;">Schedule on a beat</button>'
-
 
2725
			+ '<button class="vr-reassign" data-rid="' + r.id + '" style="font-size:11px;padding:3px 8px;border:1px solid #475569;border-radius:4px;background:none;color:#cbd5e1;cursor:pointer;">Reassign</button>'
-
 
2726
			+ '<button class="vr-reject" data-rid="' + r.id + '" style="font-size:11px;padding:3px 8px;border:none;border-radius:4px;background:#ef4444;color:#fff;cursor:pointer;">Reject</button>'
-
 
2727
			+ '</div>'
-
 
2728
			+ '</div>';
-
 
2729
	});
-
 
2730
	$('#vr-list').html(html);
-
 
2731
	$('#visit-request-panel').show();
-
 
2732
}
-
 
2733
 
-
 
2734
$(document).on('click', '#vr-refresh', function () {
-
 
2735
	loadVisitRequests();
-
 
2736
});
-
 
2737
 
-
 
2738
// Enter "schedule a visit request" mode — same handoff to the calendar chip
-
 
2739
// click that the deferred-assign flow uses.
-
 
2740
$(document).on('click', '.vr-schedule', function () {
-
 
2741
	var $b = $(this);
-
 
2742
	state.assignVisitRequest = {
-
 
2743
		id: parseInt($b.data('rid')),
-
 
2744
		leadId: parseInt($b.data('lead')),
-
 
2745
		leadName: String($b.data('leadname') || ''),
-
 
2746
		assigneeAuthId: parseInt($b.data('assignee'))
-
 
2747
	};
-
 
2748
	alert('Click a future beat-chip on the calendar to schedule "'
-
 
2749
		+ (state.assignVisitRequest.leadName || ('Lead #' + state.assignVisitRequest.leadId))
-
 
2750
		+ '" onto it.');
-
 
2751
	$('.panel-tab[data-tab="calendar"]').click();
-
 
2752
});
-
 
2753
 
-
 
2754
$(document).on('click', '.vr-reassign', function () {
-
 
2755
	var rid = $(this).data('rid');
-
 
2756
	var newId = prompt('New assignee auth user id (must be in your downline):');
-
 
2757
	if (!newId) return;
-
 
2758
	var newIdInt = parseInt(newId);
-
 
2759
	if (!newIdInt) return;
-
 
2760
	$.ajax({
-
 
2761
		url: context + '/visitRequest/' + rid + '/reassign',
-
 
2762
		type: 'POST', contentType: 'application/json',
-
 
2763
		data: JSON.stringify({newAssigneeAuthId: newIdInt}),
-
 
2764
		success: function () {
-
 
2765
			loadVisitRequests();
-
 
2766
		},
-
 
2767
		error: function (xhr) {
-
 
2768
			var msg = 'Reassign failed';
-
 
2769
			try {
-
 
2770
				var e = JSON.parse(xhr.responseText);
-
 
2771
				if (e && e.response) msg = (typeof e.response === 'string') ? e.response : (e.response.message || msg);
-
 
2772
			} catch (_) {
-
 
2773
			}
-
 
2774
			alert(msg);
-
 
2775
		}
-
 
2776
	});
-
 
2777
});
-
 
2778
 
-
 
2779
$(document).on('click', '.vr-reject', function () {
-
 
2780
	var rid = $(this).data('rid');
-
 
2781
	var reason = prompt('Reject reason (required):');
-
 
2782
	if (!reason || !reason.trim()) return;
-
 
2783
	$.ajax({
-
 
2784
		url: context + '/visitRequest/' + rid + '/reject',
-
 
2785
		type: 'POST', contentType: 'application/json',
-
 
2786
		data: JSON.stringify({rejectReason: reason.trim()}),
-
 
2787
		success: function () {
-
 
2788
			loadVisitRequests();
-
 
2789
		},
-
 
2790
		error: function (xhr) {
-
 
2791
			var msg = 'Reject failed';
-
 
2792
			try {
-
 
2793
				var e = JSON.parse(xhr.responseText);
-
 
2794
				if (e && e.response) msg = (typeof e.response === 'string') ? e.response : (e.response.message || msg);
-
 
2795
			} catch (_) {
-
 
2796
			}
-
 
2797
			alert(msg);
-
 
2798
		}
-
 
2799
	});
-
 
2800
});
-
 
2801
 
-
 
2802
function approveVisitRequestOnBeat(pg, date) {
-
 
2803
	var vr = state.assignVisitRequest;
-
 
2804
	if (!vr) return;
-
 
2805
	var today = new Date();
-
 
2806
	var todayStr = today.getFullYear() + '-' + ('0' + (today.getMonth() + 1)).slice(-2) + '-' + ('0' + today.getDate()).slice(-2);
-
 
2807
	if (date <= todayStr) {
-
 
2808
		alert('Pick a FUTURE date (after today).');
-
 
2809
		return;
-
 
2810
	}
-
 
2811
	if (!confirm('Schedule "' + (vr.leadName || ('Lead #' + vr.leadId)) + '" onto this beat on ' + date + '?')) return;
-
 
2812
	$.ajax({
-
 
2813
		url: context + '/visitRequest/' + vr.id + '/approve-schedule',
-
 
2814
		type: 'POST', contentType: 'application/json',
-
 
2815
		data: JSON.stringify({beatId: parseInt(pg), scheduleDate: date}),
-
 
2816
		success: function () {
-
 
2817
			state.assignVisitRequest = null;
-
 
2818
			alert('Scheduled. The request has been resolved.');
-
 
2819
			$('.panel-tab[data-tab="route"]').click();
-
 
2820
			loadVisitRequests();
-
 
2821
		},
-
 
2822
		error: function (xhr) {
-
 
2823
			var msg = 'Schedule failed';
-
 
2824
			try {
-
 
2825
				var er = JSON.parse(xhr.responseText);
-
 
2826
				if (er && er.response) msg = (typeof er.response === 'string') ? er.response : (er.response.message || msg);
-
 
2827
			} catch (_) {
-
 
2828
			}
-
 
2829
			alert(msg);
-
 
2830
		}
-
 
2831
	});
-
 
2832
}