2023-12-01 12:54:35 +05:30
|
|
|
import dayjs from 'dayjs';
|
|
|
|
|
import { createQueryParams, readQueryParams } from 'src/utils/QueryParamsHelper';
|
|
|
|
|
import { getAgentAllocations, getAgentLocationHistory } from './actions/LiveLocationTrackerActions';
|
|
|
|
|
import {
|
|
|
|
|
AgentDetailsPayload,
|
|
|
|
|
IAgentAllocation,
|
|
|
|
|
IAgentLocationHistory,
|
|
|
|
|
ISelectedFeedback,
|
|
|
|
|
LOCATION_TYPE_FILTERS,
|
|
|
|
|
MapPinLocation
|
|
|
|
|
} from './constants/LiveLocationTrackerInterfaces';
|
|
|
|
|
import { AGENT_LIVE_LOCATION } from './constants/LiveLocatonTrackerConstants';
|
|
|
|
|
import { NavigateFunction } from 'react-router-dom';
|
|
|
|
|
import { formatEpochTime } from 'src/utils/DateHelper';
|
|
|
|
|
import styles from './components/AgentMarker/agentMarker.module.scss';
|
|
|
|
|
import { v4 as uuidv4 } from 'uuid';
|
2024-01-24 22:47:13 +05:30
|
|
|
import { LocalStorage } from '@cp/src/utils/StorageUtils';
|
2023-12-01 12:54:35 +05:30
|
|
|
|
2024-03-08 20:21:02 +05:30
|
|
|
enum MapPinsZIndices {
|
|
|
|
|
CASES = 1,
|
|
|
|
|
AGENT = 2,
|
|
|
|
|
GENUINE_FEEDBACK = 3,
|
|
|
|
|
SUSPICIOUS_FEEDBACK = 4
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-01 12:54:35 +05:30
|
|
|
export const getAllocationPins = (
|
|
|
|
|
data: Record<string, IAgentAllocation>,
|
|
|
|
|
agentLocationHistory: IAgentLocationHistory[]
|
|
|
|
|
) => {
|
|
|
|
|
const casesLocation: MapPinLocation[] = [];
|
2024-03-08 20:21:02 +05:30
|
|
|
const feedbacksLocation = [];
|
2023-12-01 12:54:35 +05:30
|
|
|
const suspiciousFeedback: MapPinLocation[] = [];
|
|
|
|
|
const genuineFeedback: MapPinLocation[] = [];
|
2024-03-08 20:21:02 +05:30
|
|
|
const agentLocations: IAgentLocationHistory[] = [];
|
2023-12-01 12:54:35 +05:30
|
|
|
for (const lanNo in data) {
|
|
|
|
|
if (data?.[lanNo]?.location) {
|
|
|
|
|
casesLocation.push({
|
|
|
|
|
type: LOCATION_TYPE_FILTERS.CASES,
|
|
|
|
|
location: data?.[lanNo]?.location,
|
|
|
|
|
lanNo,
|
2024-03-08 20:21:02 +05:30
|
|
|
name: data?.[lanNo]?.customerName,
|
|
|
|
|
addressText: data?.[lanNo]?.addressText,
|
|
|
|
|
zIndex: MapPinsZIndices.CASES
|
2023-12-01 12:54:35 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data?.[lanNo]?.feedbacks) {
|
2024-03-08 20:21:02 +05:30
|
|
|
const feedbacks = data[lanNo].feedbacks.map(feedback => ({
|
|
|
|
|
...feedback,
|
|
|
|
|
lanNo
|
|
|
|
|
}));
|
|
|
|
|
feedbacksLocation.push(...feedbacks);
|
2023-12-01 12:54:35 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-08 20:21:02 +05:30
|
|
|
feedbacksLocation?.forEach(feedback => {
|
2023-12-01 12:54:35 +05:30
|
|
|
if (feedback.suspicious) {
|
|
|
|
|
suspiciousFeedback?.push({
|
|
|
|
|
type: LOCATION_TYPE_FILTERS.SUSPICIOUS_FEEDBACK,
|
|
|
|
|
location: feedback?.location,
|
|
|
|
|
interactionId: feedback?.interactionId,
|
|
|
|
|
lastUpdatedAt: formatEpochTime(feedback?.epochTimestamp),
|
2024-03-08 20:21:02 +05:30
|
|
|
interactionStatus: feedback?.interactionStatus,
|
|
|
|
|
lanNo: feedback?.lanNo,
|
|
|
|
|
zIndex: MapPinsZIndices.SUSPICIOUS_FEEDBACK
|
2023-12-01 12:54:35 +05:30
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
genuineFeedback?.push({
|
|
|
|
|
type: LOCATION_TYPE_FILTERS.GENUINE_FEEDBACK,
|
|
|
|
|
location: feedback?.location,
|
|
|
|
|
interactionId: feedback?.interactionId,
|
|
|
|
|
lastUpdatedAt: formatEpochTime(feedback?.epochTimestamp),
|
2024-03-08 20:21:02 +05:30
|
|
|
interactionStatus: feedback?.interactionStatus,
|
|
|
|
|
lanNo: feedback?.lanNo,
|
|
|
|
|
zIndex: MapPinsZIndices.GENUINE_FEEDBACK
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
agentLocationHistory?.forEach((locationData: IAgentLocationHistory) => {
|
|
|
|
|
agentLocations.push({
|
|
|
|
|
...locationData,
|
|
|
|
|
zIndex: MapPinsZIndices.AGENT
|
2023-12-01 12:54:35 +05:30
|
|
|
});
|
|
|
|
|
});
|
2024-03-08 20:21:02 +05:30
|
|
|
|
2023-12-01 12:54:35 +05:30
|
|
|
return {
|
|
|
|
|
casesLocation,
|
|
|
|
|
suspiciousFeedback,
|
|
|
|
|
genuineFeedback,
|
2024-03-08 20:21:02 +05:30
|
|
|
agentLocations,
|
2023-12-01 12:54:35 +05:30
|
|
|
disabledFilters: {
|
|
|
|
|
[LOCATION_TYPE_FILTERS.AGENT]: !agentLocationHistory?.length,
|
|
|
|
|
[LOCATION_TYPE_FILTERS.CASES]: !casesLocation?.length,
|
|
|
|
|
[LOCATION_TYPE_FILTERS.GENUINE_FEEDBACK]: !genuineFeedback?.length,
|
|
|
|
|
[LOCATION_TYPE_FILTERS.SUSPICIOUS_FEEDBACK]: !suspiciousFeedback?.length
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getAgentLocationFormattedData = (data: IAgentLocationHistory[]) => {
|
|
|
|
|
const agentLocationHistory: MapPinLocation[] = [];
|
|
|
|
|
data?.forEach((locationData: IAgentLocationHistory) => {
|
|
|
|
|
agentLocationHistory.push({
|
|
|
|
|
type: LOCATION_TYPE_FILTERS.AGENT,
|
|
|
|
|
location: locationData?.location,
|
|
|
|
|
lastUpdatedAt: formatEpochTime(locationData?.epochTimestamp),
|
|
|
|
|
referenceId: uuidv4()
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
return agentLocationHistory;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getMapLocations = (
|
2024-03-08 20:21:02 +05:30
|
|
|
locationFilters: Record<string, boolean>,
|
2023-12-01 12:54:35 +05:30
|
|
|
agentAllocations: Record<string, IAgentAllocation> | null,
|
|
|
|
|
agentLocationHistory: IAgentLocationHistory[]
|
|
|
|
|
) => {
|
2024-03-08 20:21:02 +05:30
|
|
|
const { casesLocation, suspiciousFeedback, genuineFeedback, agentLocations, disabledFilters } =
|
|
|
|
|
getAllocationPins(agentAllocations ?? {}, agentLocationHistory);
|
|
|
|
|
|
|
|
|
|
// Initialize an empty array to hold the combined data for all selected filters
|
|
|
|
|
let combinedData: Array<MapPinLocation | IAgentLocationHistory> = [];
|
|
|
|
|
|
|
|
|
|
// Check if the array includes specific filters and add the corresponding data
|
|
|
|
|
if (locationFilters?.[LOCATION_TYPE_FILTERS.CASES]) {
|
|
|
|
|
combinedData = [...combinedData, ...casesLocation];
|
|
|
|
|
}
|
|
|
|
|
if (locationFilters?.[LOCATION_TYPE_FILTERS.AGENT]) {
|
|
|
|
|
combinedData = [...combinedData, ...agentLocations];
|
2023-12-01 12:54:35 +05:30
|
|
|
}
|
2024-03-08 20:21:02 +05:30
|
|
|
if (locationFilters?.[LOCATION_TYPE_FILTERS.GENUINE_FEEDBACK]) {
|
|
|
|
|
combinedData = [...combinedData, ...genuineFeedback];
|
|
|
|
|
}
|
|
|
|
|
if (locationFilters?.[LOCATION_TYPE_FILTERS.SUSPICIOUS_FEEDBACK]) {
|
|
|
|
|
combinedData = [...combinedData, ...suspiciousFeedback];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If no specific filters are selected or the ALL filter is selected, combine all data
|
|
|
|
|
if (locationFilters?.[LOCATION_TYPE_FILTERS.ALL]) {
|
|
|
|
|
combinedData = [...casesLocation, ...agentLocations, ...genuineFeedback, ...suspiciousFeedback];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
data: combinedData,
|
|
|
|
|
disabledFilters
|
|
|
|
|
};
|
2023-12-01 12:54:35 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getMaxInputDate = (format: string) => {
|
|
|
|
|
return dayjs().format(format);
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-01 15:17:08 +05:30
|
|
|
// start of the month
|
|
|
|
|
export const getMinInputDate = (format: string) => {
|
|
|
|
|
return dayjs().startOf('month').format(format);
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-01 12:54:35 +05:30
|
|
|
export const getAgentDetails =
|
|
|
|
|
(payload: AgentDetailsPayload, navigate: NavigateFunction) => (dispatch: any) => {
|
|
|
|
|
const { [AGENT_LIVE_LOCATION]: queryParams = {} } = readQueryParams();
|
|
|
|
|
const url = createQueryParams({
|
|
|
|
|
[AGENT_LIVE_LOCATION]: { ...queryParams, DATE: payload?.date, SELECTED_FEEDBACK_ID: '' }
|
|
|
|
|
});
|
|
|
|
|
navigate(url);
|
|
|
|
|
dispatch(getAgentLocationHistory(payload));
|
|
|
|
|
dispatch(getAgentAllocations(payload));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const shouldZoomIn = (
|
|
|
|
|
selectedFeedback: ISelectedFeedback,
|
|
|
|
|
type: LOCATION_TYPE_FILTERS | undefined,
|
|
|
|
|
lanNo: string | undefined,
|
|
|
|
|
interactionId: string | undefined
|
|
|
|
|
) => {
|
|
|
|
|
if (!type) return false;
|
|
|
|
|
return (
|
|
|
|
|
(selectedFeedback?.lanNo === lanNo && type === LOCATION_TYPE_FILTERS.CASES) ||
|
|
|
|
|
(selectedFeedback?.interactionId === interactionId &&
|
|
|
|
|
[LOCATION_TYPE_FILTERS.SUSPICIOUS_FEEDBACK, LOCATION_TYPE_FILTERS.GENUINE_FEEDBACK].includes(
|
|
|
|
|
type
|
|
|
|
|
))
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
// [OVERLAY-POPUP] Reference: https://developers.google.com/maps/documentation/javascript/examples/overlay-popup
|
|
|
|
|
export const loadCustomPopup = (type?: string) => {
|
|
|
|
|
class CustomPopup extends google.maps.OverlayView {
|
|
|
|
|
position: google.maps.LatLng;
|
|
|
|
|
containerDiv: HTMLDivElement;
|
|
|
|
|
refId: string;
|
|
|
|
|
|
|
|
|
|
constructor(position: google.maps.LatLng, content: HTMLElement, refId: string) {
|
|
|
|
|
super();
|
|
|
|
|
this.position = position;
|
|
|
|
|
this.refId = refId;
|
|
|
|
|
content.classList.add(styles.popupBubble);
|
|
|
|
|
|
|
|
|
|
// This zero-height div is positioned at the bottom of the bubble.
|
|
|
|
|
const bubbleAnchor = document.createElement('div');
|
|
|
|
|
bubbleAnchor.classList.add(styles.popupBubbleAnchor);
|
|
|
|
|
|
|
|
|
|
if (!type) {
|
|
|
|
|
content.classList.add(styles.agentPopupBubble);
|
|
|
|
|
bubbleAnchor.classList.add(styles.agentPopupBubbleAnchor);
|
|
|
|
|
}
|
|
|
|
|
bubbleAnchor.appendChild(content);
|
|
|
|
|
|
|
|
|
|
// This zero-height div is positioned at the bottom of the tip.
|
|
|
|
|
this.containerDiv = document.createElement('div');
|
|
|
|
|
this.containerDiv.classList.add(styles.popupContainer);
|
|
|
|
|
this.containerDiv.appendChild(bubbleAnchor);
|
|
|
|
|
|
|
|
|
|
// Optionally stop clicks, etc., from bubbling up to the map.
|
|
|
|
|
CustomPopup.preventMapHitsAndGesturesFrom(this.containerDiv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Called when the popup is added to the map. */
|
|
|
|
|
onAdd() {
|
|
|
|
|
this.getPanes()!.floatPane.appendChild(this.containerDiv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Called when the popup is removed from the map. */
|
|
|
|
|
onRemove() {
|
|
|
|
|
if (this.containerDiv.parentElement) {
|
|
|
|
|
this.containerDiv.parentElement.removeChild(this.containerDiv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Called each frame when the popup needs to draw itself. */
|
|
|
|
|
draw() {
|
|
|
|
|
const divPosition = this.getProjection().fromLatLngToDivPixel(this.position)!;
|
|
|
|
|
|
|
|
|
|
// Hide the popup when it is far out of view.
|
|
|
|
|
const display =
|
|
|
|
|
Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000 ? 'block' : 'none';
|
|
|
|
|
|
|
|
|
|
if (display === 'block') {
|
|
|
|
|
this.containerDiv.style.left = divPosition.x + 'px';
|
|
|
|
|
this.containerDiv.style.top = divPosition.y + 'px';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.containerDiv.style.display !== display) {
|
|
|
|
|
this.containerDiv.style.display = display;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return CustomPopup;
|
|
|
|
|
};
|
2024-01-24 22:47:13 +05:30
|
|
|
|
|
|
|
|
export const getLiveLocationParams = (source: string) => {
|
|
|
|
|
// Get the current live-location-tracker-params from local storage
|
|
|
|
|
const currentParams = LocalStorage.getItem('live-location-tracker-params') || '';
|
|
|
|
|
|
|
|
|
|
const paramRegex = /SOURCE\*AGENT_LIVE_LOCATION=[^&]*/;
|
|
|
|
|
const hasParam = paramRegex.test(currentParams);
|
|
|
|
|
|
|
|
|
|
let updatedParams;
|
|
|
|
|
if (hasParam) {
|
|
|
|
|
updatedParams = currentParams.replace(paramRegex, `SOURCE*${AGENT_LIVE_LOCATION}=${source}`);
|
|
|
|
|
} else {
|
|
|
|
|
updatedParams =
|
|
|
|
|
currentParams + (currentParams ? '&' : '?') + `SOURCE*${AGENT_LIVE_LOCATION}=${source}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return updatedParams;
|
|
|
|
|
};
|