diff --git a/.eslintrc.json b/.eslintrc.json
index 92da60b4..d93b1aed 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -37,6 +37,7 @@
},
"rules": {
"react/react-in-jsx-scope": "off",
+ "react/display-name": "off",
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error",
"react-hooks/rules-of-hooks": "error",
diff --git a/configuration.js b/configuration.js
index ab4bd350..73b29f84 100644
--- a/configuration.js
+++ b/configuration.js
@@ -1,26 +1,27 @@
window.config = {
- BFF_SERVICE_BASE_URL: '/api',
- EXTENSION_PLUGIN_USERS_LIST: [],
- APM_BASE_URL: 'https://apm-server.np.navi-sa.in/',
- APM_APP_NAME: 'collections-portal',
- AUTH_BASE_URL: 'https://dev-dark-knight.np.navi-sa.in',
- AUTH_CLIENT_ID: 'KBDpUxvr5S',
- SENTRY_DSN: 'https://6f03f79661684b70a2e501dde312402d@glitchtip.cmd.navi-tech.in/126',
- BUILD_TIME: 0,
- ENABLE_SSO: "true",
- ENV: "dev",
- FCM_apiKey: 'AIzaSyAmUlPwmxtjehTcPBOH3HGxGP-BdWvC9JY',
- FCM_authDomain: 'longhornchat-qa.firebaseapp.com',
- FCM_databaseURL: 'https://address-verification-app-default-rtdb.firebaseio.com',
- FCM_projectId: 'longhornchat-qa',
- FCM_storageBucket: 'longhornchat-qa.appspot.com',
- FCM_messagingSenderId: '767234038375',
- FCM_appId: '1:767234038375:web:a6804341cbaa889b5c12c2',
- FCM_measurementId: 'G-CV730KBYP6',
- FCM_VapidKey: 'BBWfszq15lfSiA2IdSIi9mwo8vK52D47wlQBSHyMbdyaufuy5b13DAvNETyB-dvxROz5C_JKuOobbEfZt4Dpvzk',
- DISABLE_AMEYO_TOOLBAR: "false",
- GOOGLE_CAPTCHA_SITE_KEY: "6LezfLIlAAAAABGHea7siv00VaZhRjfcPoCEI6_c",
- GOOGLE_MAPS_KEY: '',
- GOOGLE_MAP_ID: '',
- HRC_CALL_AUTO_ACCEPT_TIMEOUT: "2",
+ BFF_SERVICE_BASE_URL: '/api',
+ EXTENSION_PLUGIN_USERS_LIST: [],
+ APM_BASE_URL: 'https://apm-server.np.navi-sa.in/',
+ APM_APP_NAME: 'collections-portal',
+ AUTH_BASE_URL: 'https://dev-dark-knight.np.navi-sa.in',
+ AUTH_CLIENT_ID: 'KBDpUxvr5S',
+ SENTRY_DSN: 'https://6f03f79661684b70a2e501dde312402d@glitchtip.cmd.navi-tech.in/126',
+ BUILD_TIME: 0,
+ ENABLE_SSO: 'true',
+ ENV: 'dev',
+ FCM_apiKey: 'AIzaSyAmUlPwmxtjehTcPBOH3HGxGP-BdWvC9JY',
+ FCM_authDomain: 'longhornchat-qa.firebaseapp.com',
+ FCM_databaseURL: 'https://address-verification-app-default-rtdb.firebaseio.com',
+ FCM_projectId: 'longhornchat-qa',
+ FCM_storageBucket: 'longhornchat-qa.appspot.com',
+ FCM_messagingSenderId: '767234038375',
+ FCM_appId: '1:767234038375:web:a6804341cbaa889b5c12c2',
+ FCM_measurementId: 'G-CV730KBYP6',
+ FCM_VapidKey:
+ 'BBWfszq15lfSiA2IdSIi9mwo8vK52D47wlQBSHyMbdyaufuy5b13DAvNETyB-dvxROz5C_JKuOobbEfZt4Dpvzk',
+ DISABLE_AMEYO_TOOLBAR: 'false',
+ GOOGLE_CAPTCHA_SITE_KEY: '6LezfLIlAAAAABGHea7siv00VaZhRjfcPoCEI6_c',
+ GOOGLE_MAPS_KEY: '',
+ GOOGLE_MAP_ID: '',
+ HRC_CALL_AUTO_ACCEPT_TIMEOUT: '2'
};
diff --git a/src/assets/styles/variables.scss b/src/assets/styles/variables.scss
index 85cfdfa9..bb95daf7 100644
--- a/src/assets/styles/variables.scss
+++ b/src/assets/styles/variables.scss
@@ -141,6 +141,7 @@
--z-index-external-sensei-toggle-tabs: 3;
--z-index-external-sensei-header: 4;
--z-index-dropdown-picker: 10;
+ --z-index-map-options: 10;
--z-index-side-logout-btn: 42;
--z-index-ameyo-disconnect-btn: 99;
--z-index-leaderboard-overlay: 99;
@@ -150,6 +151,7 @@
--z-index-autocomplete-dropdown: 100;
--z-index-ameyo-collapsible-toolbar: 100;
--z-index-autocomplete-picker: 100;
+ --z-index-maps-loader-overlay: 100;
--z-index-sticky-note: 999;
--z-index-audio-player: 1001;
--z-index-leaderboard-overlay-active: 1002;
diff --git a/src/components/AutocompleteDropdown/SingleAutocompleteDropdown.tsx b/src/components/AutocompleteDropdown/SingleAutocompleteDropdown.tsx
index 6f429606..249b56a3 100644
--- a/src/components/AutocompleteDropdown/SingleAutocompleteDropdown.tsx
+++ b/src/components/AutocompleteDropdown/SingleAutocompleteDropdown.tsx
@@ -17,6 +17,7 @@ const SingleAutocompleteDropdown = forwardRef
- {selectedOption && !disabled ? (
+ {selectedOption && !disabled && !hideCloseOption ? (
) : null}
diff --git a/src/components/AutocompleteDropdown/interfaces.tsx b/src/components/AutocompleteDropdown/interfaces.tsx
index 8d70e256..20fe614f 100644
--- a/src/components/AutocompleteDropdown/interfaces.tsx
+++ b/src/components/AutocompleteDropdown/interfaces.tsx
@@ -32,6 +32,7 @@ export interface ISingleAutocompleteDropdown extends ComponentPropsWithRef<'inpu
error?: string;
disabled?: boolean;
sortOptions?: boolean;
+ hideCloseOption?: boolean;
onSearch?: (searchTerm: string) => void;
onSelectionChange: (selectedOptions: IOption | null) => void;
customOptionTemplate?: (option: IOption) => React.ReactNode;
diff --git a/src/components/sidebar/SideBarItems.tsx b/src/components/sidebar/SideBarItems.tsx
index 83a8969a..64aeb5d2 100644
--- a/src/components/sidebar/SideBarItems.tsx
+++ b/src/components/sidebar/SideBarItems.tsx
@@ -16,7 +16,9 @@ export const DASHBOARD_URL = location.origin + '/calling-agent/dashboard';
export const ADMIN_CASES_URL = location.origin + '/admin';
export const persistQueryParamsRoutes = [
- APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.external_allocation_view
+ APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.external_allocation_view,
+ APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.external_live_location,
+ APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.live_location
];
export default SideBarItems;
diff --git a/src/components/sidebar/SideNavBar.tsx b/src/components/sidebar/SideNavBar.tsx
index 8fea287c..ec29d910 100644
--- a/src/components/sidebar/SideNavBar.tsx
+++ b/src/components/sidebar/SideNavBar.tsx
@@ -25,7 +25,7 @@ import Switch from '@navi/web-ui/lib/primitives/Switch/Switch';
import { refreshCallData, setManualLoginCreds } from 'src/reducers/humanReminderSlice';
import { ExtensionHandler } from 'src/utils/extension.utils';
import { AMEYO_STATUS_CODES } from 'src/service/naviExtension.service';
-import APP_ROUTES from 'src/layout/Routes';
+import APP_ROUTES, { APP_ROUTES_PATHS_WITH_PATH_PARAM } from 'src/layout/Routes';
import { toast } from '@navi/web-ui/lib/primitives/Toast/core/toast';
import {
AMEYO_SESSION_EVENTS,
@@ -404,7 +404,7 @@ function SideNavBar({ isDc97User, isHRCChatUser }: ISideNavbarProps) {
route={
externalAgencyPerformanceDashboard
? APP_ROUTES.SENSEI_EXTERNAL.relativePath
- : APP_ROUTES.PERFORMANCE_DASHBOARD.path
+ : APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.daily_planning
}
linkClickHandler={linkClickHandler}
currentPathname={pathname}
@@ -510,19 +510,6 @@ function SideNavBar({ isDc97User, isHRCChatUser }: ISideNavbarProps) {
/>
) : null}
- {fieldTLAndClusterAndZonalManagers ? (
-
}
- inActiveIcon={
}
- name={'Tracker'}
- route={APP_ROUTES.SENSEI.relativePath}
- linkClickHandler={linkClickHandler}
- currentPathname={pathname}
- currentSearch={search}
- clickStreamEvent={CLICKSTREAM_EVENT_NAMES.LH_FIELD_DASHBOARD_SIDE_PANEL_CLICK}
- />
- ) : null}
{isPincodeMappingVisible ? (
{
+ const queryParams = readQueryParams();
+ const agentLiveLocationParams = queryParams?.[AGENT_LIVE_LOCATION] || {};
const navigate = useNavigate();
-
const dispatch = useDispatch();
const { tabId = '' } = useParams();
- const { allLastUpdatedAt, userData, featureFlags, dailyPlanningLastUpdatedAt } = useSelector(
- (state: RootState) => ({
- allLastUpdatedAt: state?.externalDashboardSensei?.allLastUpdatedAt,
- userData: state?.common?.userData,
- featureFlags: state?.common?.featureFlags,
- dailyPlanningLastUpdatedAt: state?.sensai?.dailyPlanning?.commitmentsTable?.lastUpdatedAt
- })
- );
+ const {
+ allLastUpdatedAt,
+ featureFlags,
+ dailyPlanningLastUpdatedAt,
+ liveLocationTrackerLastUpdatedAt
+ } = useSelector((state: RootState) => ({
+ allLastUpdatedAt: state?.externalDashboardSensei?.allLastUpdatedAt,
+ featureFlags: state?.common?.featureFlags,
+ dailyPlanningLastUpdatedAt: state?.sensai?.dailyPlanning?.commitmentsTable?.lastUpdatedAt,
+ liveLocationTrackerLastUpdatedAt: state.liveLocationTracker?.lastUpdatedAt
+ }));
const {
overallPerformanceOverallMetricsTable,
@@ -51,79 +53,103 @@ const Header = () => {
agentAllocationMapViewFlag
} = featureFlags || {};
- const lastUpdatedAt = useMemo(
- () => getLastUpdatedAt({ ...allLastUpdatedAt, dailyPlanningLastUpdatedAt }, tabId),
- [tabId, allLastUpdatedAt, dailyPlanningLastUpdatedAt]
- );
- const { roles } = userData || {};
- const handleRefresh = () => {
- switch (tabId) {
- case TabsKey.OVERALL_PERFORMANCE:
- if (overallPerformanceOverallMetricsTable) dispatch(getDpdBucketData());
- if (overallPerformanceAgentPerformanceTable) dispatch(getAgentPerformanceTLData());
- break;
- case TabsKey.FIELD_GOVERNANCE:
- if (fieldGovernanceAgencyPerformanceTable) dispatch(getAgencyDetailsData());
- if (fieldGovernanceAgentPerformanceTable) dispatch(getAgentPerformanceData());
- break;
- case TabsKey.DAILY_PLANNING:
- dispatch(fetchCommitments());
- dispatch(fetchPTPDetails());
- break;
+ const { lastUpdatedAt, handleRefresh } = useMemo(() => {
+ let handleRefresh = null;
+ let lastUpdatedAt = null;
- case TabsKey.ALLOCATION_VIEW:
- if (agentAllocationMapViewFlag) dispatch(getCaseAllocationViewData());
+ switch (tabId) {
+ case TabsKey.DAILY_PLANNING: {
+ handleRefresh = () => {
+ dispatch(fetchCommitments());
+ dispatch(fetchPTPDetails());
+ };
+ lastUpdatedAt = dailyPlanningLastUpdatedAt;
+ break;
+ }
+ case TabsKey.OVERALL_PERFORMANCE: {
+ handleRefresh = () => {
+ if (overallPerformanceOverallMetricsTable) dispatch(getDpdBucketData());
+ if (overallPerformanceAgentPerformanceTable) dispatch(getAgentPerformanceTLData());
+ };
+ lastUpdatedAt = getOverallPerformanceLastUpdatedAt(allLastUpdatedAt);
+ break;
+ }
+ case TabsKey.FIELD_GOVERNANCE: {
+ handleRefresh = () => {
+ if (fieldGovernanceAgencyPerformanceTable) dispatch(getAgencyDetailsData());
+ if (fieldGovernanceAgentPerformanceTable) dispatch(getAgentPerformanceData());
+ };
+ lastUpdatedAt = getFieldGovernanceLastUpdatedAt(allLastUpdatedAt);
+ break;
+ }
+ case TabsKey.ALLOCATION_VIEW: {
+ const isAllocationFiltersSelected = Object.keys(queryParams?.allocationView || {})?.length;
+ if (!isAllocationFiltersSelected) return { lastUpdatedAt: null, handleRefresh: null };
+ handleRefresh = () => {
+ if (agentAllocationMapViewFlag) dispatch(getCaseAllocationViewData());
+ };
+ lastUpdatedAt = allLastUpdatedAt?.allocationViewLastUpdatedAt;
+ break;
+ }
+ case TabsKey.LIVE_LOCATION:
+ if (!agentLiveLocationParams?.AGENCY) {
+ return { lastUpdatedAt: null, handleRefresh: null };
+ }
+ handleRefresh = () => {
+ if (agentLiveLocationParams?.AGENTID) {
+ dispatch(
+ getAgentDetails(
+ {
+ date: agentLiveLocationParams?.DATE,
+ referenceId: agentLiveLocationParams?.AGENTID
+ },
+ navigate
+ )
+ );
+ } else {
+ const selectedTeamLead = agentLiveLocationParams[FilterTypes.TEAM_LEAD];
+ const selectedAgency = agentLiveLocationParams[FilterTypes.AGENCY];
+ const payload = {
+ agencyCodes: [selectedAgency],
+ teamLeadReferenceIds: selectedTeamLead ? [selectedTeamLead] : []
+ };
+ dispatch(getAgentsLocations(payload, false));
+ }
+ };
+ lastUpdatedAt = liveLocationTrackerLastUpdatedAt;
break;
default:
break;
}
- };
- const enableAgentLiveLocation = useMemo(() => showAgentsLiveLocation(roles), [roles]);
-
- const handleLiveLocationClick = () => {
- addClickstreamEvent(
- PERFORMANCE_DASHBOARD_CLICKSTREAM[PERFORMANCE_DASHBOARD_EVENTS.LH_AGENT_TRACKING_CLICKED],
- { userId: userData?.referenceId }
- );
- const updatedParams = getLiveLocationParams(LIVE_LOCATION_SOURCE.EXTERNAL_SENSEI);
- navigate(`${APP_ROUTES.LIVE_LOCATION_TRACKER.path}${updatedParams}`);
- };
-
- const queryParams = readQueryParams();
- const isAllocationFiltersSelected = Object.keys(queryParams?.allocationView || {})?.length;
- const showExtraInfo = tabId === TabsKey.ALLOCATION_VIEW ? isAllocationFiltersSelected : true;
+ return { lastUpdatedAt, handleRefresh };
+ }, [
+ tabId,
+ queryParams?.SELECTED_FEEDBACK_ID,
+ allLastUpdatedAt,
+ dailyPlanningLastUpdatedAt,
+ liveLocationTrackerLastUpdatedAt
+ ]);
return (
Agent Dashboard
- {showExtraInfo ? (
- <>
+
+ {lastUpdatedAt ? (
Last updated at{' '}
{lastUpdatedAt
? dateFormat(new Date(lastUpdatedAt), DateFormat.LONG_DATE_FORMAT_WITH_TIME)
: '--'}
+ ) : null}
+ {handleRefresh ? (
- >
- ) : null}
-
-
- {enableAgentLiveLocation ? (
-
- }
- variant="text"
- >
- Live location tracker
-
-
- ) : null}
+ ) : null}
+
);
diff --git a/src/pages/ExternalDashboardSensei/constants.ts b/src/pages/ExternalDashboardSensei/constants.ts
index 79081ccd..ec5e9ec3 100644
--- a/src/pages/ExternalDashboardSensei/constants.ts
+++ b/src/pages/ExternalDashboardSensei/constants.ts
@@ -28,6 +28,7 @@ export enum TabsKey {
OVERALL_PERFORMANCE = 'overall_performance',
FIELD_GOVERNANCE = 'field_governance',
DAILY_PLANNING = 'daily_planning',
+ LIVE_LOCATION = 'live_location',
ALLOCATION_VIEW = 'allocation_view'
}
diff --git a/src/pages/ExternalDashboardSensei/index.tsx b/src/pages/ExternalDashboardSensei/index.tsx
index 510cf052..10934372 100644
--- a/src/pages/ExternalDashboardSensei/index.tsx
+++ b/src/pages/ExternalDashboardSensei/index.tsx
@@ -10,7 +10,7 @@ import { RootState } from '@cp/src/store';
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
import Header from './components/Header';
-import APP_ROUTES, { APP_ROUTES_PATHS_WITH_PATH_PARAM } from '@cp/src/layout/Routes';
+import APP_ROUTES from '@cp/src/layout/Routes';
import { interpolatePathParams } from '@cp/src/utils/interpolate';
import { LocalStorage } from '@cp/src/utils/StorageUtils';
@@ -26,12 +26,7 @@ const ExternalDashboardSensei = () => {
const paramsMap: Record = LocalStorage.getItem('paramsMap') || {};
const handleTabChange = (updateTabId: TabItemKey) => {
- let currentParams = '';
if (tabId !== updateTabId) {
- if (updateTabId === TabsKey.ALLOCATION_VIEW) {
- currentParams =
- paramsMap[APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.external_allocation_view] || '';
- }
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_FIELD_DASHBOARD_TAB_SWITCH, {
currentTab: tabId,
newTab: updateTabId
@@ -39,6 +34,7 @@ const ExternalDashboardSensei = () => {
const updatedUrl = interpolatePathParams(APP_ROUTES.SENSEI_EXTERNAL.path, {
tabId: String(updateTabId)
});
+ const currentParams = paramsMap[updatedUrl] || '';
navigate(updatedUrl + currentParams);
}
};
@@ -57,17 +53,14 @@ const ExternalDashboardSensei = () => {
TabsKey.FIELD_GOVERNANCE,
TabsKey.OVERALL_PERFORMANCE,
TabsKey.DAILY_PLANNING,
- TabsKey.ALLOCATION_VIEW
+ TabsKey.ALLOCATION_VIEW,
+ TabsKey.LIVE_LOCATION
].includes(tabId as TabsKey);
if (isTabAccessNotAvailable || noTabSelected) {
- const tabId = dashboardTabs?.[0]?.relativePath;
- let currentParams = '';
- if (tabId.includes(TabsKey.ALLOCATION_VIEW)) {
- currentParams =
- paramsMap[APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.external_allocation_view] || '';
- }
- navigate(tabId + currentParams);
+ const relativePath = dashboardTabs?.[0]?.relativePath;
+ const currentParams = paramsMap[relativePath] || '';
+ navigate(relativePath + currentParams);
}
}, [tabId, dashboardTabs]);
diff --git a/src/pages/ExternalDashboardSensei/types.ts b/src/pages/ExternalDashboardSensei/types.ts
index 360eea32..42138a46 100644
--- a/src/pages/ExternalDashboardSensei/types.ts
+++ b/src/pages/ExternalDashboardSensei/types.ts
@@ -155,6 +155,7 @@ export interface IAllLastUpdateAt {
agentPerformanceTlLastUpdatedAt: number;
dailyPlanningLastUpdatedAt: number;
allocationViewLastUpdatedAt: number;
+ liveLocationTrackerLastUpdatedAt: string | Date;
}
export interface IExternalSenseiDashboard {
diff --git a/src/pages/ExternalDashboardSensei/utils.tsx b/src/pages/ExternalDashboardSensei/utils.tsx
index fa80288b..81e8bda0 100644
--- a/src/pages/ExternalDashboardSensei/utils.tsx
+++ b/src/pages/ExternalDashboardSensei/utils.tsx
@@ -7,10 +7,16 @@ import AllocationView from '../MapAllocationView';
import OverallPerformance from './OverallPerformance';
import { FIELD_GOVERNANCE_TABS_VALUE, IAllLastUpdateAt } from './types';
import ExternalDailyPlanning from './components/DailyPlanning';
+import LiveLocationTracker from '../LiveLocationTracker';
export const getTabs = (featureFlags: IFeatureFlags, tabId: string) => {
- const { overallPerformanceTab, fieldGovernanceTab, fcmDashboard, agentAllocationMapViewFlag } =
- featureFlags || {};
+ const {
+ overallPerformanceTab,
+ fieldGovernanceTab,
+ fcmDashboard,
+ agentAllocationMapViewFlag,
+ liveLocationTrackerFeatureFlag
+ } = featureFlags || {};
const tabs = [];
@@ -41,6 +47,15 @@ export const getTabs = (featureFlags: IFeatureFlags, tabId: string) => {
});
}
+ if (liveLocationTrackerFeatureFlag) {
+ tabs.push({
+ key: TabsKey.LIVE_LOCATION,
+ value: 'Live location tracker',
+ component: ,
+ relativePath: '/sensei-external/live_location'
+ });
+ }
+
if (agentAllocationMapViewFlag) {
tabs.push({
key: TabsKey.ALLOCATION_VIEW,
@@ -121,6 +136,30 @@ export const setInitialOverallPerformanceTabParams = (
navigate(updatedParams);
};
+export const getOverallPerformanceLastUpdatedAt = (allLastUpdatedAt: IAllLastUpdateAt) => {
+ const { dpdBucketLastUpdatedAt, agentPerformanceTlLastUpdatedAt } = allLastUpdatedAt;
+
+ if (!dpdBucketLastUpdatedAt) return agentPerformanceTlLastUpdatedAt;
+ if (!agentPerformanceTlLastUpdatedAt) return dpdBucketLastUpdatedAt;
+
+ if (dpdBucketLastUpdatedAt < agentPerformanceTlLastUpdatedAt) {
+ return dpdBucketLastUpdatedAt;
+ }
+ return agentPerformanceTlLastUpdatedAt;
+};
+
+export const getFieldGovernanceLastUpdatedAt = (allLastUpdatedAt: IAllLastUpdateAt) => {
+ const { agencyDetailsLastUpdatedAt, agentPerformanceLastUpdatedAt } = allLastUpdatedAt;
+
+ if (!agencyDetailsLastUpdatedAt) return agentPerformanceLastUpdatedAt;
+ if (!agentPerformanceLastUpdatedAt) return agencyDetailsLastUpdatedAt;
+
+ if (agencyDetailsLastUpdatedAt < agentPerformanceLastUpdatedAt) {
+ return agencyDetailsLastUpdatedAt;
+ }
+ return agentPerformanceLastUpdatedAt;
+};
+
export const getLastUpdatedAt = (allLastUpdatedAt: IAllLastUpdateAt, tabId: string) => {
const {
dpdBucketLastUpdatedAt,
@@ -128,7 +167,8 @@ export const getLastUpdatedAt = (allLastUpdatedAt: IAllLastUpdateAt, tabId: stri
agentPerformanceLastUpdatedAt,
agentPerformanceTlLastUpdatedAt,
dailyPlanningLastUpdatedAt,
- allocationViewLastUpdatedAt
+ allocationViewLastUpdatedAt,
+ liveLocationTrackerLastUpdatedAt
} = allLastUpdatedAt;
if (tabId === TabsKey.OVERALL_PERFORMANCE) {
@@ -158,4 +198,7 @@ export const getLastUpdatedAt = (allLastUpdatedAt: IAllLastUpdateAt, tabId: stri
if (tabId === TabsKey.ALLOCATION_VIEW) {
return allocationViewLastUpdatedAt;
}
+ if (tabId === TabsKey.LIVE_LOCATION) {
+ return liveLocationTrackerLastUpdatedAt;
+ }
};
diff --git a/src/pages/LiveLocationTracker/components/AgentMarker/index.tsx b/src/pages/LiveLocationTracker/components/AgentMarker/index.tsx
index 8e3d6890..994e69ae 100644
--- a/src/pages/LiveLocationTracker/components/AgentMarker/index.tsx
+++ b/src/pages/LiveLocationTracker/components/AgentMarker/index.tsx
@@ -3,14 +3,22 @@ import useMap from '../../hooks/useMap';
import styles from './agentMarker.module.scss';
import Tooltip from '../Tooltip';
import { getNameInitials } from 'src/utils/commonUtils';
-import { AgentMarkerProps, ColorMap } from '../../constants/LiveLocationTrackerInterfaces';
-import { useSelector } from 'react-redux';
+import {
+ AgentMarkerProps,
+ ColorMap,
+ LOCATION_TYPE_FILTERS
+} from '../../constants/LiveLocationTrackerInterfaces';
+import { useDispatch, useSelector } from 'react-redux';
import { RootState } from '../../../../store';
import { addClickstreamEvent } from 'src/service/clickStreamEventService';
import { AgentTrackingEvents } from 'src/service/clickStream.constant';
import AgentMarkerIcon from './AgentMarkerIcon';
import { shouldZoomIn, loadCustomPopup } from '../../utils';
import { ICustomPopup } from '../../types';
+import { AGENT_LIVE_LOCATION, PinColorMapping } from '../../constants/LiveLocatonTrackerConstants';
+import { setSelectedFeedback } from '../../reducers/LiveLocationTrackerSlice';
+import { createQueryParams, readQueryParams } from '@cp/src/utils/QueryParamsHelper';
+import { useNavigate } from 'react-router-dom';
export const colorMap: ColorMap = {
yellow: {
@@ -38,19 +46,11 @@ export interface InfoWindowRef {
close: () => void;
}
-const AgentMarker = ({
- color,
- position,
- name,
- lastUpdatedAt,
- referenceId,
- type,
- lanNo,
- interactionId,
- interactionStatus,
- activeCustomPopup,
- refId
-}: AgentMarkerProps) => {
+export const AgentMarker = ({ agentLocation, activeCustomPopup, position }: AgentMarkerProps) => {
+ const { name, referenceId, type, lanNo, interactionId, activityLevel, location, zIndex } =
+ agentLocation;
+ const refId = referenceId || lanNo || interactionId || '';
+ const color = PinColorMapping[activityLevel] ?? 'red';
const map = useMap();
const markerRef = useRef(null);
const markerInstance: any = useRef(null);
@@ -59,15 +59,24 @@ const AgentMarker = ({
const initials = getNameInitials(name);
const isInfoWindowVisible = useRef(false);
const customPopupRef = useRef(null);
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const { [AGENT_LIVE_LOCATION]: queryParams = {} } = readQueryParams();
- const { selectedAgent, isShowAllInfoWindow, userId, selectedFeedback } = useSelector(
- (store: RootState) => ({
+ const { selectedAgent, isShowAllInfoWindow, userId, selectedFeedback, agentAllocations } =
+ useSelector((store: RootState) => ({
selectedAgent: store?.liveLocationTracker?.selectedAgent,
isShowAllInfoWindow: store?.liveLocationTracker?.showAllInfoWindows,
userId: store?.common?.userData?.referenceId,
- selectedFeedback: store?.liveLocationTracker?.selectedFeedback
- })
- );
+ selectedFeedback: store?.liveLocationTracker?.selectedFeedback,
+ agentAllocations: store?.liveLocationTracker?.agentAllocations?.data
+ }));
+
+ const selectedFeedbackRef = useRef(selectedFeedback);
+
+ useEffect(() => {
+ selectedFeedbackRef.current = selectedFeedback;
+ }, [selectedFeedback]);
useEffect(() => {
if (
@@ -103,6 +112,33 @@ const AgentMarker = ({
}
}, [selectedFeedback]);
+ const feedbackClickHandler = () => {
+ if (!lanNo) {
+ return;
+ }
+ const selectedFeedback = selectedFeedbackRef.current;
+ const data = agentAllocations?.[lanNo];
+ const isSelectedFeedbackClicked = selectedFeedback?.interactionId === interactionId;
+ dispatch(
+ isSelectedFeedbackClicked
+ ? setSelectedFeedback(null)
+ : setSelectedFeedback({
+ lanNo,
+ interactionId,
+ feedbackLocation: location,
+ caseLocation: data?.location,
+ isSuspicious: type === LOCATION_TYPE_FILTERS.SUSPICIOUS_FEEDBACK
+ })
+ );
+ const newQueryParams = {
+ ...queryParams
+ };
+ const url = createQueryParams({
+ [AGENT_LIVE_LOCATION]: newQueryParams
+ });
+ navigate(url);
+ };
+
useEffect(() => {
if (map?.state?.mapLoaderInstance) {
map?.state?.mapLoaderInstance
@@ -111,7 +147,8 @@ const AgentMarker = ({
markerInstance.current = new AdvancedMarkerElement({
position: position,
map: map.state.mapInstance,
- content: markerRef.current
+ content: markerRef.current,
+ zIndex
});
const CustomPopup = loadCustomPopup(type);
if (tooltipRef.current) {
@@ -133,6 +170,12 @@ const AgentMarker = ({
name
}
});
+ if (
+ type === LOCATION_TYPE_FILTERS.GENUINE_FEEDBACK ||
+ type === LOCATION_TYPE_FILTERS.SUSPICIOUS_FEEDBACK
+ ) {
+ feedbackClickHandler();
+ }
} else {
addClickstreamEvent(AgentTrackingEvents.LH_AGENT_TRACKING_PIN_CLICKED, {
userId,
@@ -181,12 +224,8 @@ const AgentMarker = ({
diff --git a/src/pages/LiveLocationTracker/components/AgentsAccordion/AccordionBody.tsx b/src/pages/LiveLocationTracker/components/AgentsAccordion/AccordionBody.tsx
index 250a0115..48fa8ce0 100644
--- a/src/pages/LiveLocationTracker/components/AgentsAccordion/AccordionBody.tsx
+++ b/src/pages/LiveLocationTracker/components/AgentsAccordion/AccordionBody.tsx
@@ -1,4 +1,3 @@
-import { BorderedInput } from '@navi/web-ui/lib/primitives';
import React, { useEffect, useMemo, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
@@ -15,7 +14,6 @@ import styles from './index.module.scss';
import { _map } from 'src/utils/commonUtils';
import { IFeedback, LOCATION_TYPE_FILTERS } from '../../constants/LiveLocationTrackerInterfaces';
import { setSelectedFeedback } from '../../reducers/LiveLocationTrackerSlice';
-import useMap from '../../hooks/useMap';
import DateTimePickerComponent from '@cp/src/components/DateTimePicker/DateTimePickerComponent';
import { DATE_TIME_TYPE } from '@cp/src/components/DateTimePicker/constants';
@@ -35,6 +33,7 @@ const AccordionBody = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const polylineRef = useRef(null);
+ const feedbackRefs = useRef>({});
const { [AGENT_LIVE_LOCATION]: queryParams = {} } = readQueryParams();
@@ -86,6 +85,19 @@ const AccordionBody = () => {
return lanKeys;
}, [agentAllocations]);
+ useEffect(() => {
+ if (selectedFeedback && !queryParams?.SELECTED_FEEDBACK_ID) {
+ // scroll to the selected feedback view
+ const feedbackRef = feedbackRefs.current[selectedFeedback.lanNo];
+ if (feedbackRef) {
+ feedbackRef.scrollIntoView({
+ behavior: 'smooth',
+ block: 'start'
+ });
+ }
+ }
+ }, [selectedFeedback, queryParams?.SELECTED_FEEDBACK_ID]);
+
const feedbackClickHandler = (lanNo: string, feedback: IFeedback) => {
const data = agentAllocations?.[lanNo];
const isSelectedFeedbackClicked = selectedFeedback?.interactionId === feedback?.interactionId;
@@ -103,7 +115,7 @@ const AccordionBody = () => {
);
addClickstreamEvent(
isSelectedFeedbackClicked
- ? AgentTrackingEvents.LH_MAP_FEEDBACK_CLICKED
+ ? AgentTrackingEvents.LH_MAP_FEEDBACK_DOUBLE_CLICKED
: AgentTrackingEvents.LH_MAP_FEEDBACK_CLICKED,
{
userId,
@@ -129,7 +141,7 @@ const AccordionBody = () => {
if (selectedAgent) {
return (
- <>
+
ACTIVITY LOG
@@ -143,18 +155,21 @@ const AccordionBody = () => {
containerClasses={styles.activityDatePicker}
/>
- {agentAllocationsLoading ? null : lanKeys?.length ? (
- lanKeys.map(lanNo => (
-
feedbackClickHandler(lanNo, feedback)}
- />
- ))
- ) : (
- No Feedbacks Found
- )}
- >
+
+ {agentAllocationsLoading ? null : lanKeys?.length ? (
+ lanKeys.map(lanNo => (
+
(feedbackRefs.current[lanNo] = el)}
+ lanNo={lanNo}
+ feedbackClickHandler={feedback => feedbackClickHandler(lanNo, feedback)}
+ />
+ ))
+ ) : (
+ No Feedbacks Found
+ )}
+
+
);
}
@@ -169,9 +184,11 @@ const AccordionBody = () => {
))}
{agentLocations?.length ? (
- agentLocations?.map(item => (
-
- ))
+
+ {agentLocations?.map(item => (
+
+ ))}
+
) : (
No Agents Found
)}
diff --git a/src/pages/LiveLocationTracker/components/AgentsAccordion/AgentDetail.tsx b/src/pages/LiveLocationTracker/components/AgentsAccordion/AgentDetail.tsx
index 1b2fb0b4..c0eaf529 100644
--- a/src/pages/LiveLocationTracker/components/AgentsAccordion/AgentDetail.tsx
+++ b/src/pages/LiveLocationTracker/components/AgentsAccordion/AgentDetail.tsx
@@ -36,7 +36,7 @@ const AgentDetail = (props: AgentDetailProps) => {
userId: state?.common?.userData?.referenceId
}));
const { selectedAgent } = liveLocationTracker || {};
- const { name = '', activityLevel, referenceId, lastUpdatedAt } = item || {};
+ const { name = '', activityLevel, referenceId, lastUpdatedAt, totalAllocatedCases } = item || {};
const color = PinColorMapping[activityLevel];
@@ -133,6 +133,7 @@ const AgentDetail = (props: AgentDetailProps) => {
last seen {date} {time}
)}
+ Allocated: {totalAllocatedCases || 0}
{showNextIcon && }
diff --git a/src/pages/LiveLocationTracker/components/AgentsAccordion/FeedbackDetail.tsx b/src/pages/LiveLocationTracker/components/AgentsAccordion/FeedbackDetail.tsx
index b601a87b..3f6c8738 100644
--- a/src/pages/LiveLocationTracker/components/AgentsAccordion/FeedbackDetail.tsx
+++ b/src/pages/LiveLocationTracker/components/AgentsAccordion/FeedbackDetail.tsx
@@ -1,5 +1,5 @@
+import React from 'react';
import { useSelector } from 'react-redux';
-import FeedbackTriangleIcon from 'src/assets/icons/FeedbackTriangleIcon';
import RedirectionIcon from 'src/assets/icons/RedirectionIcon';
import APP_ROUTES from 'src/layout/Routes';
import { TAB_KEYS } from 'src/pages/CaseDetails/constants';
@@ -20,7 +20,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from 'src/components/TooltipV
import ChatBubbleExclamation from 'src/assets/images/icons/ChatBubbleExclamation';
import ChatBubble from 'src/assets/images/icons/ChatBubble';
-const FeedbackDetail = (props: FeedbackDetailProps) => {
+const FeedbackDetail = React.forwardRef((props, ref) => {
const { lanNo, feedbackClickHandler } = props;
const {
agentAllocations: { data: agentAllocations },
@@ -63,6 +63,7 @@ const FeedbackDetail = (props: FeedbackDetailProps) => {
const isFeedbackSelected = selectedFeedback?.interactionId === feedback?.interactionId;
return (
{
})}
>
);
-};
+});
export default FeedbackDetail;
diff --git a/src/pages/LiveLocationTracker/components/AgentsAccordion/index.module.scss b/src/pages/LiveLocationTracker/components/AgentsAccordion/index.module.scss
index 78ded760..2a0647b5 100644
--- a/src/pages/LiveLocationTracker/components/AgentsAccordion/index.module.scss
+++ b/src/pages/LiveLocationTracker/components/AgentsAccordion/index.module.scss
@@ -1,5 +1,6 @@
.agentsAccordionContainer {
width: 260px;
+ font-family: 'Inter';
.accordion {
border-radius: 8px;
@@ -19,10 +20,7 @@
.accordionBody {
margin: 0;
- }
-
- .content {
- max-height: calc(100vh - 230px);
+ overflow-y: hidden;
}
}
@@ -153,8 +151,6 @@
.dateFilterContainer {
padding: 12px 12px 6px 12px;
border-bottom: 1px solid var(--navi-color-gray-border);
- position: sticky;
- top: 0;
background: var(--navi-color-gray-bg-primary);
.activityLog {
diff --git a/src/pages/LiveLocationTracker/components/GoogleMapsContainer/GoogleMapsContainer.module.scss b/src/pages/LiveLocationTracker/components/GoogleMapsContainer/GoogleMapsContainer.module.scss
index 1278f2c5..3a186464 100644
--- a/src/pages/LiveLocationTracker/components/GoogleMapsContainer/GoogleMapsContainer.module.scss
+++ b/src/pages/LiveLocationTracker/components/GoogleMapsContainer/GoogleMapsContainer.module.scss
@@ -1,4 +1,4 @@
.googleMapContainer {
width: 100%;
- height: calc(100vh - 60px);
+ height: calc(100vh - 100px); // 100px is the height of the header
}
diff --git a/src/pages/LiveLocationTracker/components/LocationAgencySearch/index.module.scss b/src/pages/LiveLocationTracker/components/LocationAgencySearch/index.module.scss
new file mode 100644
index 00000000..bf080a1b
--- /dev/null
+++ b/src/pages/LiveLocationTracker/components/LocationAgencySearch/index.module.scss
@@ -0,0 +1,68 @@
+@import '../../../../assets/styles/animations';
+
+.blurMapContainer {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 2;
+
+ .blurMapImg {
+ object-fit: cover;
+ width: 100%;
+ height: 100%;
+ filter: blur(12.5px);
+ }
+}
+
+.mapPlaceholderContainer {
+ padding: 20px 16px;
+ border-radius: 8px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ left: 50%;
+ top: 25%;
+ transform: translate(-50%);
+
+ span {
+ color: var(--black-base);
+ }
+}
+
+.loader {
+ border: 8px solid var(--grayscale-background-secondary);
+ border-top: 8px solid var(--navi-color-blue-light);
+ border-radius: 50%;
+ width: 60px;
+ height: 60px;
+ animation: spin 2s linear infinite;
+}
+
+.autoCompleteMainContainer {
+ width: 100%;
+}
+
+.autoCompleteContainer {
+ background: white;
+ margin-top: 4px;
+}
+
+.optionLabel {
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 20px;
+ letter-spacing: -0.0125em;
+ color: var(--navi-color-gray-c1);
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.selectPickerWrapper {
+ padding: 0;
+ max-height: calc((100vh - 100px - 25%) / 2);
+}
diff --git a/src/pages/LiveLocationTracker/components/LocationAgencySearch/index.tsx b/src/pages/LiveLocationTracker/components/LocationAgencySearch/index.tsx
new file mode 100644
index 00000000..5bf96373
--- /dev/null
+++ b/src/pages/LiveLocationTracker/components/LocationAgencySearch/index.tsx
@@ -0,0 +1,72 @@
+import styles from './index.module.scss';
+import { Typography } from '@primitives';
+import { SelectPickerValue } from '@cp/src/components/interfaces';
+import { useNavigate } from 'react-router-dom';
+import { QueryParamTypeMapping } from '../../../ExternalDashboardSensei/constants';
+import { createQueryParams, readQueryParams } from '@cp/src/utils/QueryParamsHelper';
+import { SingleSelectAutoComplete } from '@navi/web-ui/lib/components';
+import { useDispatch, useSelector } from 'react-redux';
+import { RootState } from '@cp/src/store';
+import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
+import { AgentTrackingEvents } from '@cp/src/service/clickStream.constant';
+import AllocationMapPlaceholder from '@cp/src/pages/MapAllocationView/components/AllocationMapPlaceholder';
+import { AGENT_LIVE_LOCATION } from '../../constants/LiveLocatonTrackerConstants';
+import { FilterTypes } from '../../constants/LiveLocationTrackerInterfaces';
+import { useEffect } from 'react';
+import { getFilterData } from '../../actions/LiveLocationTrackerActions';
+
+const LocationAgencySearch = () => {
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const params = readQueryParams();
+ const { filterTypes, filterData } = useSelector((state: RootState) => state.liveLocationTracker);
+ const { data = [] } = filterData['AGENCY'];
+ useEffect(() => {
+ // Get agency filter data
+ dispatch(getFilterData());
+ }, []);
+
+ const { caseAllocation } = useSelector((state: RootState) => ({
+ caseAllocation: state.caseAllocation
+ }));
+ const { reportingAgencies } = caseAllocation || {};
+
+ const agencyDropdownHandler = (action: SelectPickerValue) => {
+ if (!action) return;
+ const queryParams = { ...params };
+
+ if (!queryParams?.[AGENT_LIVE_LOCATION]) queryParams[AGENT_LIVE_LOCATION] = {};
+
+ if (!queryParams[AGENT_LIVE_LOCATION]?.[FilterTypes.AGENCY]) {
+ queryParams[AGENT_LIVE_LOCATION][FilterTypes.AGENCY] = action;
+ }
+
+ // addClickstreamEvent(AgentTrackingEvents.LH_MAP_AUTOCOMPLETE_FILTER_APPLIED, {
+ // value: action,
+ // page: ALLOCATION_PAGE
+ // });
+ const updatedParams = createQueryParams(queryParams);
+ navigate(updatedParams);
+ };
+
+ return (
+
+
+ Select an agency to view locations
+
+ {option.label}
}
+ />
+
+ );
+};
+
+export default LocationAgencySearch;
diff --git a/src/pages/LiveLocationTracker/components/MapPlaceholder/mapPlaceholder.module.scss b/src/pages/LiveLocationTracker/components/MapPlaceholder/mapPlaceholder.module.scss
index 45ee24ed..2c1f5b49 100644
--- a/src/pages/LiveLocationTracker/components/MapPlaceholder/mapPlaceholder.module.scss
+++ b/src/pages/LiveLocationTracker/components/MapPlaceholder/mapPlaceholder.module.scss
@@ -1,10 +1,10 @@
.blurMapContainer {
- position: absolute;
+ position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
- z-index: 10;
+ z-index: var(--z-index-maps-loader-overlay);
.blurMapImg {
object-fit: cover;
@@ -29,6 +29,9 @@
}
.blurImageWrapper {
+ position: absolute;
+ top: 0px;
+ left: 0px;
z-index: 2;
}
diff --git a/src/pages/LiveLocationTracker/components/MapView/index.tsx b/src/pages/LiveLocationTracker/components/MapView/index.tsx
index 32db79a6..d95ab18d 100644
--- a/src/pages/LiveLocationTracker/components/MapView/index.tsx
+++ b/src/pages/LiveLocationTracker/components/MapView/index.tsx
@@ -4,11 +4,7 @@ import AgentMarker from '../AgentMarker';
import useMap from '../../hooks/useMap';
import { useSelector } from 'react-redux';
import { RootState } from 'src/store';
-import {
- AGENT_LIVE_LOCATION,
- FilterMap,
- PinColorMapping
-} from '../../constants/LiveLocatonTrackerConstants';
+import { AGENT_LIVE_LOCATION, FilterMap } from '../../constants/LiveLocatonTrackerConstants';
import styles from './index.module.scss';
import { readQueryParams } from 'src/utils/QueryParamsHelper';
import {
@@ -18,8 +14,6 @@ import {
} from '../../constants/LiveLocationTrackerInterfaces';
import { INHOUSE_AGENCY_CODE } from 'src/components/constant';
import MapPlaceholder from '../MapPlaceholder';
-import AgentsAccordion from '../AgentsAccordion';
-import PinFilters from '../PinFilters';
import { ICustomPopup } from '../../types';
const defaultMapOptions = {
@@ -48,7 +42,6 @@ function MapView() {
const {
filterTypes,
loadingFilters,
- selectedAgent,
agentLocations,
mapLocations,
agentLocationsLoading,
@@ -84,7 +77,7 @@ function MapView() {
map.state.mapInstance.setCenter(bounds.getCenter()); //or use custom center
map.state.mapInstance.fitBounds(bounds);
const currentZoomLevel = map.state.mapInstance.getZoom();
- const newLevel = currentZoomLevel - 1;
+ const newLevel = currentZoomLevel - 2;
map.state.mapInstance.setZoom(newLevel);
});
}
@@ -165,17 +158,19 @@ function MapView() {
}, [queryParams?.DATE]);
useEffect(() => {
- if (!queryParams.SELECTED_FEEDBACK_ID && polylineRef.current) {
+ if (!queryParams?.SELECTED_FEEDBACK_ID && polylineRef.current) {
polylineRef.current.setMap(null);
return;
}
- zoomOverFeedback(selectedFeedback?.feedbackLocation, selectedFeedback?.caseLocation);
- drawLineBetweenFeedbackAndCase(
- selectedFeedback?.feedbackLocation,
- selectedFeedback?.caseLocation,
- selectedFeedback?.isSuspicious
- );
- }, [queryParams?.SELECTED_FEEDBACK_ID]);
+ if (queryParams?.SELECTED_FEEDBACK_ID) {
+ zoomOverFeedback(selectedFeedback?.feedbackLocation, selectedFeedback?.caseLocation);
+ drawLineBetweenFeedbackAndCase(
+ selectedFeedback?.feedbackLocation,
+ selectedFeedback?.caseLocation,
+ selectedFeedback?.isSuspicious
+ );
+ }
+ }, [queryParams?.SELECTED_FEEDBACK_ID, selectedFeedback]);
useEffect(() => {
if (!mapLocations?.length) {
@@ -212,66 +207,39 @@ function MapView() {
const showBlurMap = !mapLocations?.length && !loadingFilters && !isFiltersSelected();
- const googleMapsContainer = useMemo(
- () => (
+ const googleMapsContainer = useMemo(() => {
+ return (
{mapLocations?.map(agentLocation => {
- const {
- location,
- name = '',
- activityLevel,
- referenceId,
- lastUpdatedAt,
- type,
- lanNo,
- interactionId,
- interactionStatus
- } = agentLocation || {};
+ const { location } = agentLocation || {};
const { latitude: lat, longitude: lng } = location || {};
if (!lat || !lng) {
return null;
}
- const refId = referenceId || lanNo || interactionId || '';
- const color = PinColorMapping[activityLevel] ?? 'red';
return (
);
})}
- ),
- [mapLocations]
- );
+ );
+ }, [mapLocations]);
return (
-
+
{googleMapsContainer}
- {filteredAgentLocations?.length === 0 && !agentLocationsLoading ? (
+ {!filteredAgentLocations?.length && !agentLocationsLoading ? (
) : null}
{showBlurMap && !agentLocationsLoading ? (
) : null}
- {agentLocations?.length && (
-
- )}
- {selectedAgent &&
}
);
}
diff --git a/src/pages/LiveLocationTracker/components/PinFilters/index.module.scss b/src/pages/LiveLocationTracker/components/PinFilters/index.module.scss
index 82daa4b4..3d206dde 100644
--- a/src/pages/LiveLocationTracker/components/PinFilters/index.module.scss
+++ b/src/pages/LiveLocationTracker/components/PinFilters/index.module.scss
@@ -1,7 +1,4 @@
.pinFiltersContainer {
- position: absolute;
- top: 16px;
- left: 292px; // width of Agents Panel
background-color: var(--bg-primary);
padding: 8px 8px 8px 12px;
border-radius: 8px;
diff --git a/src/pages/LiveLocationTracker/components/PinFilters/index.tsx b/src/pages/LiveLocationTracker/components/PinFilters/index.tsx
index 20164e96..87bd3e63 100644
--- a/src/pages/LiveLocationTracker/components/PinFilters/index.tsx
+++ b/src/pages/LiveLocationTracker/components/PinFilters/index.tsx
@@ -1,4 +1,4 @@
-import { useEffect } from 'react';
+import React, { useEffect, useMemo } from 'react';
import styles from './index.module.scss';
import cx from 'classnames';
import { createQueryParams, readQueryParams } from 'src/utils/QueryParamsHelper';
@@ -25,11 +25,20 @@ const PinFilters = () => {
const { [AGENT_LIVE_LOCATION]: queryParams = {} } = readQueryParams();
const navigate = useNavigate();
const dispatch = useDispatch();
+ // map of selected filters
+ const locationTypeParams = useMemo(() => {
+ const locationTypeArray = queryParams?.LOCATION_TYPE?.split(',') || [];
+ const locationTypeParams: Record
= {};
+ locationTypeArray.forEach((locationType: LOCATION_TYPE_FILTERS) => {
+ locationTypeParams[locationType] = true;
+ });
+ return locationTypeParams;
+ }, [queryParams?.LOCATION_TYPE]);
useEffect(() => {
if (!agentLocationHistory?.loading && !agentAllocations?.loading) {
const { data, disabledFilters } = getMapLocations(
- queryParams?.LOCATION_TYPE,
+ locationTypeParams,
agentAllocations?.data,
agentLocationHistory?.data
);
@@ -48,13 +57,42 @@ const PinFilters = () => {
const filterClickHandler = (value: string) => {
if (disabledFilters[value]) return;
+
+ let updatedFilters = queryParams?.LOCATION_TYPE;
+
+ if (value === LOCATION_TYPE_FILTERS.ALL && updatedFilters === LOCATION_TYPE_FILTERS.ALL) {
+ return;
+ }
+
addClickstreamEvent(AgentTrackingEvents.LH_MAP_FILTER_CLICKED, {
userId,
agentId: queryParams?.AGENTID,
filterValue: value
});
+
+ if (locationTypeParams?.[value]) {
+ // remove filter if already applied
+ updatedFilters = updatedFilters?.replace(value, '');
+ } else {
+ if (value === LOCATION_TYPE_FILTERS.ALL) {
+ // remove other filter if all filter is applied
+ updatedFilters = LOCATION_TYPE_FILTERS.ALL;
+ } else {
+ // remove all filter if any other filter is applied
+ updatedFilters = updatedFilters?.replace(LOCATION_TYPE_FILTERS.ALL, '');
+ updatedFilters += `,${value}`;
+ }
+ }
+
+ // remove leading and trailing commas
+ updatedFilters = updatedFilters?.replace(/^,+|,+$/g, '');
+
const url = createQueryParams({
- [AGENT_LIVE_LOCATION]: { ...queryParams, LOCATION_TYPE: value, SELECTED_FEEDBACK_ID: '' }
+ [AGENT_LIVE_LOCATION]: {
+ ...queryParams,
+ LOCATION_TYPE: updatedFilters || LOCATION_TYPE_FILTERS.ALL,
+ SELECTED_FEEDBACK_ID: ''
+ }
});
navigate(url);
};
@@ -63,10 +101,10 @@ const PinFilters = () => {
Filter by:
{locationsPinFilters.map(filter => (
- <>
+
filterClickHandler(filter?.value)}
@@ -75,7 +113,7 @@ const PinFilters = () => {
{filter.label}
{filter.value === LOCATION_TYPE_FILTERS.ALL && }
- >
+
))}
);
diff --git a/src/pages/LiveLocationTracker/components/Tooltip/index.tsx b/src/pages/LiveLocationTracker/components/Tooltip/index.tsx
index 961e145e..56eb5433 100644
--- a/src/pages/LiveLocationTracker/components/Tooltip/index.tsx
+++ b/src/pages/LiveLocationTracker/components/Tooltip/index.tsx
@@ -1,46 +1,83 @@
+import React from 'react';
import { Typography } from '@navi/web-ui/lib/primitives';
import styles from './tooltip.module.scss';
import { forwardRef } from 'react';
import { DateFormat, dateFormat, formatDate, isToday } from 'src/utils/DateHelper';
-import { LOCATION_TYPE_FILTERS } from '../../constants/LiveLocationTrackerInterfaces';
+import {
+ IAgentLocation,
+ LOCATION_TYPE_FILTERS
+} from '../../constants/LiveLocationTrackerInterfaces';
import RedirectionIcon from 'src/assets/icons/RedirectionIcon';
-import { useSelector } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
import APP_ROUTES from 'src/layout/Routes';
import { TAB_KEYS } from 'src/pages/CaseDetails/constants';
import { RootState } from 'src/store';
import { interpolatePathParams } from 'src/utils/interpolate';
import { CloseIcon } from '@navi/web-ui/lib/icons';
+import { getMaxInputDate } from '../../utils';
+import { createQueryParams, readQueryParams } from '@cp/src/utils/QueryParamsHelper';
+import { AGENT_LIVE_LOCATION } from '../../constants/LiveLocatonTrackerConstants';
+import { useNavigate } from 'react-router-dom';
+import { setSelectedAgent } from '../../reducers/LiveLocationTrackerSlice';
+import {
+ getAgentAllocations,
+ getAgentLocationHistory
+} from '../../actions/LiveLocationTrackerActions';
type ToolTipProps = {
- name: string;
- lastUpdateActivity: string;
- style?: any;
- type?: LOCATION_TYPE_FILTERS;
- lanNo?: string;
- interactionStatus?: string;
+ agentLocation: IAgentLocation;
+ style?: React.CSSProperties;
closeTooltip: () => void;
};
-function Tooltip(props: ToolTipProps, ref: any) {
+const Tooltip = forwardRef((props: ToolTipProps, ref: any) => {
+ const { agentLocation, style, closeTooltip }: ToolTipProps = props;
const {
name,
- lastUpdateActivity,
- style,
+ lastUpdatedAt,
type,
lanNo,
interactionStatus,
- closeTooltip
- }: ToolTipProps = props;
-
- const date = isToday(lastUpdateActivity)
- ? 'Today'
- : formatDate(lastUpdateActivity, DateFormat.DD_MMM);
- const time = dateFormat(new Date(lastUpdateActivity), DateFormat.HH_mm_ampm) || '---';
+ addressText,
+ phoneNumber,
+ referenceId
+ } = agentLocation;
+ const date = isToday(lastUpdatedAt) ? 'Today' : formatDate(lastUpdatedAt, DateFormat.DD_MMM);
+ const time = dateFormat(new Date(lastUpdatedAt), DateFormat.HH_mm_ampm) || '---';
+ const { liveLocationTracker, userId } = useSelector((state: RootState) => ({
+ liveLocationTracker: state.liveLocationTracker,
+ userId: state?.common?.userData?.referenceId
+ }));
+ const { selectedAgent } = liveLocationTracker || {};
const {
agentAllocations: { data: agentAllocations }
} = useSelector((state: RootState) => state.liveLocationTracker);
+ const { [AGENT_LIVE_LOCATION]: queryParams = {} } = readQueryParams();
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+
+ const selectAgentHandler = () => {
+ if (!selectedAgent) {
+ const maxInputDate = getMaxInputDate('YYYY-MM-DD');
+ const payload = { date: maxInputDate, referenceId };
+
+ const url = createQueryParams({
+ [AGENT_LIVE_LOCATION]: {
+ ...queryParams,
+ AGENTID: referenceId,
+ LOCATION_TYPE: LOCATION_TYPE_FILTERS.ALL,
+ DATE: maxInputDate
+ }
+ });
+ navigate(url);
+ dispatch(setSelectedAgent(agentLocation));
+ dispatch(getAgentLocationHistory(payload));
+ dispatch(getAgentAllocations(payload));
+ }
+ };
+
const viewCaseHandler = () => {
if (lanNo) {
const data = agentAllocations?.[lanNo];
@@ -68,9 +105,12 @@ function Tooltip(props: ToolTipProps, ref: any) {
{lanNo}
+
+ {addressText}
+
);
@@ -82,8 +122,21 @@ function Tooltip(props: ToolTipProps, ref: any) {
{name && (
-
- {name}
+
+
+ {name}
+
+
+
+ )}
+ {phoneNumber && (
+
+ {phoneNumber}
)}
{interactionStatus && (
@@ -98,6 +151,6 @@ function Tooltip(props: ToolTipProps, ref: any) {
)}
);
-}
+});
-export default forwardRef(Tooltip);
+export default Tooltip;
diff --git a/src/pages/LiveLocationTracker/components/Tooltip/tooltip.module.scss b/src/pages/LiveLocationTracker/components/Tooltip/tooltip.module.scss
index 44257539..7722f68e 100644
--- a/src/pages/LiveLocationTracker/components/Tooltip/tooltip.module.scss
+++ b/src/pages/LiveLocationTracker/components/Tooltip/tooltip.module.scss
@@ -4,17 +4,15 @@
border-radius: 8px;
display: flex;
flex-direction: column;
- align-items: center;
justify-content: center;
- text-align: center;
.nameText {
font-size: 17px;
font-weight: 500;
line-height: 24px;
letter-spacing: -0.2px;
- margin-bottom: 5px;
- margin-right: 16px;
+ text-align: center;
+ text-wrap: nowrap;
}
.lastUpdatedAt {
@@ -24,7 +22,8 @@
line-height: 20px;
letter-spacing: -0.13px;
color: var(--navi-color-gray-c2);
- margin-right: 16px;
+ padding-right: 16px;
+ text-align: center;
}
.casesNameText {
@@ -33,11 +32,11 @@
font-weight: 500;
line-height: 18px;
letter-spacing: -0.12px;
- margin-right: 16px;
+ padding-right: 18px;
}
.lanNo {
- color: var(--navi-color-gray-c3);
+ color: var(--navi-color-gray-c2);
font-size: 12px;
font-weight: 400;
line-height: 18px;
@@ -50,13 +49,33 @@
font-weight: 400;
line-height: 18px;
letter-spacing: -0.12px;
- margin-right: 16px;
+ text-align: center;
+ margin-right: 8px;
+ }
+
+ .phoneNumber {
+ color: var(--navi-color-gray-c2);
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 18px;
+ letter-spacing: -0.12px;
+ text-align: center;
+ }
+
+ .addressText {
+ color: var(--navi-color-gray-c3);
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 18px;
+ margin-top: 4px;
+ letter-spacing: -0.12px;
}
.viewCaseCtaWrapper {
display: flex;
align-items: center;
- justify-content: center;
+ margin-top: 2px;
+ width: fit-content;
cursor: pointer;
}
@@ -71,7 +90,7 @@
.closeIcon {
position: absolute;
- padding: 8px;
+ padding: 8px 8px 4px 4px;
top: 2px;
right: 0px;
cursor: pointer;
diff --git a/src/pages/LiveLocationTracker/components/TopBar/Filters.tsx b/src/pages/LiveLocationTracker/components/TopBar/Filters.tsx
index 3390c5a6..b49a44f3 100644
--- a/src/pages/LiveLocationTracker/components/TopBar/Filters.tsx
+++ b/src/pages/LiveLocationTracker/components/TopBar/Filters.tsx
@@ -1,17 +1,13 @@
-import { useEffect, useMemo } from 'react';
+import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import {
- getAgentsLocations,
- getFilterData,
- getFilterTypes
-} from '../../actions/LiveLocationTrackerActions';
+import { getFilterData } from '../../actions/LiveLocationTrackerActions';
import { RootState } from 'src/store';
import { FilterTypes } from '../../constants/LiveLocationTrackerInterfaces';
import { _map } from 'src/utils/commonUtils';
import styles from './TopBar.module.scss';
import { FilterMap, AGENT_LIVE_LOCATION } from '../../constants/LiveLocatonTrackerConstants';
import { createQueryParams, readQueryParams } from 'src/utils/QueryParamsHelper';
-import { useNavigate, useSearchParams } from 'react-router-dom';
+import { useNavigate } from 'react-router-dom';
import {
setAgentLocations,
setFilterData,
@@ -19,73 +15,22 @@ import {
setSelectedAgent
} from '../../reducers/LiveLocationTrackerSlice';
import SingleAutocompleteDropdown from 'src/components/AutocompleteDropdown/SingleAutocompleteDropdown';
-import { INHOUSE_AGENCY_CODE } from 'src/components/constant';
import { addClickstreamEvent } from 'src/service/clickStreamEventService';
import { AgentTrackingEvents } from 'src/service/clickStream.constant';
const Filters = () => {
const dispatch = useDispatch();
- const { filterTypes, filterData, agentLocations } = useSelector(
- (state: RootState) => state.liveLocationTracker
- );
+ const { filterTypes, filterData } = useSelector((state: RootState) => state.liveLocationTracker);
const userId = useSelector((state: RootState) => state.common.userData?.referenceId);
- const [searchParams] = useSearchParams();
const { filters } = filterTypes;
const { [AGENT_LIVE_LOCATION]: queryParams = {} } = readQueryParams();
const navigate = useNavigate();
- const pinsPayload = useMemo(() => {
- const selectedAgency =
- queryParams[FilterTypes.AGENCY] || filterData[FilterTypes.AGENCY]?.data[0]?.value;
- const selectedTeamLead = queryParams[FilterTypes.TEAM_LEAD];
- return {
- agencyCodes: [selectedAgency],
- teamLeadReferenceIds: selectedTeamLead ? [selectedTeamLead] : []
- };
- }, [searchParams]);
-
useEffect(() => {
// Get agency filter data
dispatch(getFilterData());
}, [filterTypes]);
- useEffect(() => {
- if (!filters?.length) {
- dispatch(getFilterTypes());
- return;
- }
-
- const applyFilterRecursively = (filterType: FilterTypes) => {
- const selectedFilter = queryParams[filterType];
- const { nextFilter } = FilterMap[filterType];
-
- if (!selectedFilter) {
- return;
- }
-
- // If next filter is not selected or not in the list of filters, we need to fetch the pins data
- if (!nextFilter || !filters.includes(nextFilter)) {
- dispatch(getAgentsLocations(pinsPayload));
- return;
- }
-
- if (filterType === FilterTypes.AGENCY) {
- dispatch(getAgentsLocations(pinsPayload));
- }
-
- const payload = {
- type: nextFilter,
- agencyCodes: [selectedFilter]
- };
- dispatch(getFilterData(payload));
-
- // recursively call the function to apply the next filter
- applyFilterRecursively(nextFilter);
- };
-
- applyFilterRecursively(FilterTypes.AGENCY);
- }, [queryParams?.AGENCY, queryParams?.TEAM_LEAD, filters]);
-
const handleFilterChange = (value: string, filterType: FilterTypes) => {
dispatch(setMapLocations({ data: [] }));
const nextFilter = FilterMap[filterType]?.nextFilter;
@@ -173,6 +118,7 @@ const Filters = () => {
containerClasses={styles.dropdown}
disabled={disabled}
sortOptions
+ hideCloseOption={filterType === FilterTypes.AGENCY}
/>
);
})}
diff --git a/src/pages/LiveLocationTracker/components/TopBar/TopBar.module.scss b/src/pages/LiveLocationTracker/components/TopBar/TopBar.module.scss
index cbc2cddd..062ddc5b 100644
--- a/src/pages/LiveLocationTracker/components/TopBar/TopBar.module.scss
+++ b/src/pages/LiveLocationTracker/components/TopBar/TopBar.module.scss
@@ -67,12 +67,11 @@
.filters {
display: flex;
- align-items: center;
- justify-content: center;
gap: 12px;
.dropdown {
background-color: var(--bg-primary);
+ box-shadow: var(--box-shadow-3) !important;
width: 250px;
height: 36px;
}
diff --git a/src/pages/LiveLocationTracker/components/TopBar/index.tsx b/src/pages/LiveLocationTracker/components/TopBar/index.tsx
index 837f699d..b5133441 100644
--- a/src/pages/LiveLocationTracker/components/TopBar/index.tsx
+++ b/src/pages/LiveLocationTracker/components/TopBar/index.tsx
@@ -15,17 +15,12 @@ import { createQueryParams, readQueryParams } from '../../../../utils/QueryParam
import { getAgentsLocations } from '../../actions/LiveLocationTrackerActions';
import { RootState } from '../../../../store';
import dayjs from 'dayjs';
-import relativeTime from 'dayjs/plugin/relativeTime';
-import isToday from 'dayjs/plugin/isToday';
import usePolling from '../../../../hooks/usePolling';
import { getAgentDetails } from '../../utils';
import { resetMapData, setSelectedAgent } from '../../reducers/LiveLocationTrackerSlice';
import { INHOUSE_AGENCY_CODE } from 'src/components/constant';
import { useMemo } from 'react';
-dayjs.extend(relativeTime);
-dayjs.extend(isToday);
-
function TopBar() {
const navigate = useNavigate();
const dispatch = useDispatch();
diff --git a/src/pages/LiveLocationTracker/constants/LiveLocationTrackerInterfaces.ts b/src/pages/LiveLocationTracker/constants/LiveLocationTrackerInterfaces.ts
index fbd56571..c61cbe11 100644
--- a/src/pages/LiveLocationTracker/constants/LiveLocationTrackerInterfaces.ts
+++ b/src/pages/LiveLocationTracker/constants/LiveLocationTrackerInterfaces.ts
@@ -43,6 +43,7 @@ export interface IAgentAllocation {
customerName: string;
location: ILocation;
feedbacks: IFeedback[];
+ addressText: string;
}
export interface ILocation {
@@ -54,6 +55,7 @@ export interface IAgentLocationHistory {
referenceId: string;
location: ILocation;
epochTimestamp: number;
+ zIndex?: number;
}
export interface IAgentLocation {
@@ -68,6 +70,8 @@ export interface IAgentLocation {
lanNo?: string;
interactionId?: string;
interactionStatus?: string;
+ addressText?: string;
+ totalAllocatedCases: number;
}
interface IFilterTypes {
@@ -91,6 +95,8 @@ export interface MapPinLocation {
interactionId?: string;
interactionStatus?: string;
referenceId?: string;
+ addressText?: string;
+ zIndex?: number;
}
export enum FilterTypes {
@@ -142,20 +148,12 @@ export type ColorMap = Record<
>;
export interface AgentMarkerProps {
- color: string;
- name: string;
- lastUpdatedAt: string;
position: {
lat: number;
lng: number;
};
- referenceId: string;
- type?: LOCATION_TYPE_FILTERS;
- lanNo?: string;
- interactionId?: string;
- interactionStatus?: string;
activeCustomPopup: React.MutableRefObject;
- refId: string;
+ agentLocation: IAgentLocation;
}
export enum LOCATION_TYPE_FILTERS {
diff --git a/src/pages/LiveLocationTracker/index.module.scss b/src/pages/LiveLocationTracker/index.module.scss
index 7bac8f1d..6b3bf5b5 100644
--- a/src/pages/LiveLocationTracker/index.module.scss
+++ b/src/pages/LiveLocationTracker/index.module.scss
@@ -2,7 +2,31 @@
position: relative;
.overlay {
- z-index: 10;
+ z-index: var(--z-index-maps-loader-overlay);
+ width: 100%;
+ height: 100%;
+ position: fixed;
+ }
+
+ .agentFilters {
+ position: absolute;
+ top: 24px;
+ left: 298px;
+ z-index: var(--z-index-map-options);
+ }
+
+ .agentAccordion {
+ position: absolute;
+ top: 24px;
+ left: 24px;
+ z-index: var(--z-index-map-options);
+ }
+
+ .pinFilters {
+ position: absolute;
+ top: 24px;
+ left: 298px;
+ z-index: var(--z-index-map-options);
}
}
diff --git a/src/pages/LiveLocationTracker/index.tsx b/src/pages/LiveLocationTracker/index.tsx
index ef0cb356..b48bad62 100644
--- a/src/pages/LiveLocationTracker/index.tsx
+++ b/src/pages/LiveLocationTracker/index.tsx
@@ -1,17 +1,28 @@
-import TopBar from './components/TopBar';
import MapView from './components/MapView';
import Footer from './components/Footer/Footer';
import styles from './index.module.scss';
import Loader from 'src/components/Loader/Loader';
import { RootState } from 'src/store';
-import { useSelector } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
import cx from 'classnames';
-import { useEffect } from 'react';
+import { useEffect, useMemo } from 'react';
import { addClickstreamEvent } from 'src/service/clickStreamEventService';
import { AgentTrackingEvents } from 'src/service/clickStream.constant';
import MapPlaceholder from './components/MapPlaceholder';
import { readQueryParams } from 'src/utils/QueryParamsHelper';
-import { AGENT_LIVE_LOCATION } from './constants/LiveLocatonTrackerConstants';
+import { AGENT_LIVE_LOCATION, FilterMap } from './constants/LiveLocatonTrackerConstants';
+import Filters from './components/TopBar/Filters';
+import AgentsAccordion from './components/AgentsAccordion';
+import PinFilters from './components/PinFilters';
+import {
+ getAgentsLocations,
+ getFilterData,
+ getFilterTypes
+} from './actions/LiveLocationTrackerActions';
+import { FilterTypes } from './constants/LiveLocationTrackerInterfaces';
+import { useSearchParams } from 'react-router-dom';
+import { resetMapData } from './reducers/LiveLocationTrackerSlice';
+import LocationAgencySearch from './components/LocationAgencySearch';
function LiveLocationTracker() {
const {
@@ -20,20 +31,42 @@ function LiveLocationTracker() {
selectedAgent,
agentLocationHistory: { loading: isAgentLocationHistoryloading },
agentAllocations: { loading: isAgentAllocationsloading },
- agentLocationsLoading
+ agentLocations,
+ agentLocationsLoading,
+ filterTypes,
+ filterData
} = useSelector((state: RootState) => ({
loadingFilters: state.liveLocationTracker.loadingFilters,
userId: state?.common?.userData?.referenceId,
selectedAgent: state.liveLocationTracker.selectedAgent,
agentLocationHistory: state.liveLocationTracker.agentLocationHistory,
agentAllocations: state.liveLocationTracker.agentAllocations,
- agentLocationsLoading: state.liveLocationTracker.agentLocationsLoading
+ agentLocationsLoading: state.liveLocationTracker.agentLocationsLoading,
+ agentLocations: state.liveLocationTracker.agentLocations,
+ filterTypes: state.liveLocationTracker.filterTypes,
+ filterData: state.liveLocationTracker.filterData
}));
-
+ const { filters } = filterTypes || {};
+ const dispatch = useDispatch();
+ const [searchParams] = useSearchParams();
const { [AGENT_LIVE_LOCATION]: queryParams = {} } = readQueryParams();
+ const pinsPayload = useMemo(() => {
+ const selectedAgency =
+ queryParams[FilterTypes.AGENCY] || filterData[FilterTypes.AGENCY]?.data[0]?.value;
+ const selectedTeamLead = queryParams[FilterTypes.TEAM_LEAD];
+ return {
+ agencyCodes: [selectedAgency],
+ teamLeadReferenceIds: selectedTeamLead ? [selectedTeamLead] : []
+ };
+ }, [searchParams]);
+
useEffect(() => {
addClickstreamEvent(AgentTrackingEvents.LH_AGENT_TRACKING_PAGE_LOAD, { userId });
+ return () => {
+ // reset map data on unmount
+ dispatch(resetMapData());
+ };
}, []);
useEffect(() => {
@@ -41,18 +74,78 @@ function LiveLocationTracker() {
localStorage.setItem('live-location-tracker-params', window.location.search);
}, [queryParams]);
+ useEffect(() => {
+ if (!filters?.length) {
+ dispatch(getFilterTypes());
+ return;
+ }
+
+ const applyFilterRecursively = (filterType: FilterTypes) => {
+ const selectedFilter = queryParams[filterType];
+ const { nextFilter } = FilterMap[filterType];
+
+ if (!selectedFilter) {
+ return;
+ }
+
+ // If next filter is not selected or not in the list of filters, we need to fetch the pins data
+ if (!nextFilter || !filters.includes(nextFilter)) {
+ dispatch(getAgentsLocations(pinsPayload));
+ return;
+ }
+
+ if (filterType === FilterTypes.AGENCY) {
+ dispatch(getAgentsLocations(pinsPayload));
+ }
+
+ const payload = {
+ type: nextFilter,
+ agencyCodes: [selectedFilter]
+ };
+ dispatch(getFilterData(payload));
+
+ // recursively call the function to apply the next filter
+ applyFilterRecursively(nextFilter);
+ };
+
+ applyFilterRecursively(FilterTypes.AGENCY);
+ }, [queryParams?.AGENCY, queryParams?.TEAM_LEAD, filters]);
+
+ const mapLoading =
+ isAgentAllocationsloading || isAgentLocationHistoryloading || agentLocationsLoading;
+
+ if (!queryParams?.AGENCY)
+ return (
+
+
+
+ );
return (
-
+ {!selectedAgent && !mapLoading ? (
+
+
+
+ ) : null}
+ {!mapLoading ? (
+
+ ) : null}
+ {selectedAgent && !mapLoading ? (
+
+ ) : null}
- {(isAgentAllocationsloading || isAgentLocationHistoryloading || agentLocationsLoading) && (
+ {mapLoading && (
)}
diff --git a/src/pages/LiveLocationTracker/reducers/LiveLocationTrackerSlice.ts b/src/pages/LiveLocationTracker/reducers/LiveLocationTrackerSlice.ts
index a94671d2..39c24f40 100644
--- a/src/pages/LiveLocationTracker/reducers/LiveLocationTrackerSlice.ts
+++ b/src/pages/LiveLocationTracker/reducers/LiveLocationTrackerSlice.ts
@@ -112,6 +112,7 @@ const liveLocationTrackerSlice = createSlice({
...state.agentAllocations,
...action.payload
};
+ state.lastUpdatedAt = new Date(getServerTime()).toString();
},
setSelectedFeedback: (state, action) => {
state.selectedFeedback = action.payload;
diff --git a/src/pages/LiveLocationTracker/utils.tsx b/src/pages/LiveLocationTracker/utils.tsx
index 1b612095..0dfd4658 100644
--- a/src/pages/LiveLocationTracker/utils.tsx
+++ b/src/pages/LiveLocationTracker/utils.tsx
@@ -5,7 +5,6 @@ import {
AgentDetailsPayload,
IAgentAllocation,
IAgentLocationHistory,
- IFeedback,
ISelectedFeedback,
LOCATION_TYPE_FILTERS,
MapPinLocation
@@ -17,37 +16,53 @@ import styles from './components/AgentMarker/agentMarker.module.scss';
import { v4 as uuidv4 } from 'uuid';
import { LocalStorage } from '@cp/src/utils/StorageUtils';
+enum MapPinsZIndices {
+ CASES = 1,
+ AGENT = 2,
+ GENUINE_FEEDBACK = 3,
+ SUSPICIOUS_FEEDBACK = 4
+}
+
export const getAllocationPins = (
data: Record
,
agentLocationHistory: IAgentLocationHistory[]
) => {
const casesLocation: MapPinLocation[] = [];
- const feedbacksLocation: IFeedback[] = [];
+ const feedbacksLocation = [];
const suspiciousFeedback: MapPinLocation[] = [];
const genuineFeedback: MapPinLocation[] = [];
+ const agentLocations: IAgentLocationHistory[] = [];
for (const lanNo in data) {
if (data?.[lanNo]?.location) {
casesLocation.push({
type: LOCATION_TYPE_FILTERS.CASES,
location: data?.[lanNo]?.location,
lanNo,
- name: data?.[lanNo]?.customerName
+ name: data?.[lanNo]?.customerName,
+ addressText: data?.[lanNo]?.addressText,
+ zIndex: MapPinsZIndices.CASES
});
}
if (data?.[lanNo]?.feedbacks) {
- feedbacksLocation.push(...data[lanNo].feedbacks);
+ const feedbacks = data[lanNo].feedbacks.map(feedback => ({
+ ...feedback,
+ lanNo
+ }));
+ feedbacksLocation.push(...feedbacks);
}
}
- feedbacksLocation?.forEach((feedback: IFeedback) => {
+ feedbacksLocation?.forEach(feedback => {
if (feedback.suspicious) {
suspiciousFeedback?.push({
type: LOCATION_TYPE_FILTERS.SUSPICIOUS_FEEDBACK,
location: feedback?.location,
interactionId: feedback?.interactionId,
lastUpdatedAt: formatEpochTime(feedback?.epochTimestamp),
- interactionStatus: feedback?.interactionStatus
+ interactionStatus: feedback?.interactionStatus,
+ lanNo: feedback?.lanNo,
+ zIndex: MapPinsZIndices.SUSPICIOUS_FEEDBACK
});
return;
}
@@ -56,13 +71,24 @@ export const getAllocationPins = (
location: feedback?.location,
interactionId: feedback?.interactionId,
lastUpdatedAt: formatEpochTime(feedback?.epochTimestamp),
- interactionStatus: feedback?.interactionStatus
+ interactionStatus: feedback?.interactionStatus,
+ lanNo: feedback?.lanNo,
+ zIndex: MapPinsZIndices.GENUINE_FEEDBACK
});
});
+
+ agentLocationHistory?.forEach((locationData: IAgentLocationHistory) => {
+ agentLocations.push({
+ ...locationData,
+ zIndex: MapPinsZIndices.AGENT
+ });
+ });
+
return {
casesLocation,
suspiciousFeedback,
genuineFeedback,
+ agentLocations,
disabledFilters: {
[LOCATION_TYPE_FILTERS.AGENT]: !agentLocationHistory?.length,
[LOCATION_TYPE_FILTERS.CASES]: !casesLocation?.length,
@@ -86,38 +112,39 @@ export const getAgentLocationFormattedData = (data: IAgentLocationHistory[]) =>
};
export const getMapLocations = (
- locationFilter: string,
+ locationFilters: Record,
agentAllocations: Record | null,
agentLocationHistory: IAgentLocationHistory[]
) => {
- const { casesLocation, suspiciousFeedback, genuineFeedback, disabledFilters } = getAllocationPins(
- agentAllocations ?? {},
- agentLocationHistory
- );
+ const { casesLocation, suspiciousFeedback, genuineFeedback, agentLocations, disabledFilters } =
+ getAllocationPins(agentAllocations ?? {}, agentLocationHistory);
- switch (locationFilter) {
- case LOCATION_TYPE_FILTERS.AGENT:
- return { data: agentLocationHistory, disabledFilters };
+ // Initialize an empty array to hold the combined data for all selected filters
+ let combinedData: Array = [];
- case LOCATION_TYPE_FILTERS.GENUINE_FEEDBACK:
- return { data: genuineFeedback, disabledFilters };
-
- case LOCATION_TYPE_FILTERS.SUSPICIOUS_FEEDBACK:
- return { data: suspiciousFeedback, disabledFilters };
-
- case LOCATION_TYPE_FILTERS.CASES:
- return { data: casesLocation, disabledFilters };
- default:
- return {
- data: [
- ...casesLocation,
- ...agentLocationHistory,
- ...genuineFeedback,
- ...suspiciousFeedback
- ],
- disabledFilters
- };
+ // 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];
+ }
+ 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
+ };
};
export const getMaxInputDate = (format: string) => {
diff --git a/src/pages/auth/constants/AuthConstants.ts b/src/pages/auth/constants/AuthConstants.ts
index 42c73cc1..a862652d 100644
--- a/src/pages/auth/constants/AuthConstants.ts
+++ b/src/pages/auth/constants/AuthConstants.ts
@@ -68,7 +68,7 @@ export enum Roles {
ROLE_TELE_ALLOCATIONS_ACCESS = 'ROLE_TELE_ALLOCATIONS_ACCESS',
ROLE_COLLECTION_HEAD = 'ROLE_COLLECTION_HEAD',
NAVI_INHOUSE_FIELD_CLUSTER_MANAGER = 'NAVI_INHOUSE_FIELD_CLUSTER_MANAGER',
- NAVI_INHOUSE_FIELD_ZONAL_MANAGER = 'NAVI_INHOUSE_FIELD_ZONAL_MANAGER',
+ NAVI_INHOUSE_FIELD_ZONAL_MANAGER = 'ROLE_NAVI_INHOUSE_FIELD_ZONAL_MANAGER',
ROLE_NAVI_FIELD_AGENCY_TEAM_LEAD = 'ROLE_NAVI_FIELD_AGENCY_TEAM_LEAD',
ROLE_HUMAN_REMINDER_TEAM_LEAD = 'ROLE_HUMAN_REMINDER_TEAM_LEAD',
ROLE_HUMAN_REMINDER_CHAT_AGENT = 'ROLE_HUMAN_REMINDER_CHAT_AGENT',
diff --git a/src/pages/sensei/index.tsx b/src/pages/sensei/index.tsx
index cc3b2e21..bd8948fa 100644
--- a/src/pages/sensei/index.tsx
+++ b/src/pages/sensei/index.tsx
@@ -1,5 +1,5 @@
import Tabs, { TabItem } from '@navi/web-ui/lib/components/Tabs';
-import { useEffect } from 'react';
+import { useEffect, useMemo } from 'react';
import { TABS as SENSEI_TABS, TAB_KEYS } from './Tabs';
import { RootState } from '@cp/src/store';
@@ -11,19 +11,49 @@ import { useDispatch, useSelector } from 'react-redux';
import { useNavigate, useParams } from 'react-router-dom';
import APP_ROUTES from 'src/layout/Routes';
import { DateFormat, dateFormat } from 'src/utils/DateHelper';
-import { fetchCommitments, fetchPTPDetails } from './actions';
+import {
+ fetchCommitments,
+ fetchPTPDetails,
+ getOverallMetricInput,
+ getOverallMetricOutput
+} from './actions';
import styles from './index.module.scss';
+import { getAgentDetails, getLiveLocationParams } from '../LiveLocationTracker/utils';
+import { AGENT_LIVE_LOCATION } from '../LiveLocationTracker/constants/LiveLocatonTrackerConstants';
+import { readQueryParams } from '@cp/src/utils/QueryParamsHelper';
+import { FilterTypes } from '../LiveLocationTracker/constants/LiveLocationTrackerInterfaces';
+import { getAgentsLocations } from '../LiveLocationTracker/actions/LiveLocationTrackerActions';
+import LiveLocationTracker from '../LiveLocationTracker';
+import { LocalStorage } from '@cp/src/utils/StorageUtils';
const Index = () => {
const { tabId = '' } = useParams();
const navigate = useNavigate();
const dispatch = useDispatch();
- const { lastUpdatedAt } = useSelector((state: RootState) => ({
- lastUpdatedAt: state?.sensai?.dailyPlanning?.commitmentsTable?.lastUpdatedAt
+ const { [AGENT_LIVE_LOCATION]: queryParams = {} } = readQueryParams();
+ const {
+ lastUpdatedAtDailyPlanning,
+ lastUpdatedAtOverallMetric,
+ lastUpdatedAtLiveLocation,
+ featureFlags
+ } = useSelector((state: RootState) => ({
+ lastUpdatedAtDailyPlanning: state?.sensai?.dailyPlanning?.commitmentsTable?.lastUpdatedAt,
+ lastUpdatedAtOverallMetric: state?.sensai?.overallMetrics?.inputTable?.lastUpdatedAt,
+ lastUpdatedAtLiveLocation: state?.liveLocationTracker?.lastUpdatedAt,
+ featureFlags: state?.common?.featureFlags
}));
- const handleTabChange = (tabId: TabItemKey) => {
- // to add this function once we have other tabs
- navigate(interpolatePathParams(APP_ROUTES.SENSEI.path, { tabId }));
+ const { liveLocationTrackerFeatureFlag } = featureFlags || {};
+
+ const paramsMap: Record = LocalStorage.getItem('paramsMap') || {};
+
+ const handleTabChange = (updateTabId: TabItemKey) => {
+ if (tabId !== updateTabId) {
+ const updatedUrl = interpolatePathParams(APP_ROUTES.SENSEI.path, {
+ tabId: String(updateTabId)
+ });
+ const currentParams = paramsMap[updatedUrl] || '';
+ navigate(updatedUrl + currentParams);
+ }
};
useEffect(() => {
@@ -32,41 +62,98 @@ const Index = () => {
}
}, [tabId]);
- const handleRefresh = () => {
+ const { lastUpdatedAt, handleRefresh } = useMemo(() => {
+ let handleRefresh = null;
+ let lastUpdatedAt = null;
+
switch (tabId) {
- case TAB_KEYS.DAILY_PLANNING:
- dispatch(fetchCommitments());
- dispatch(fetchPTPDetails());
+ case TAB_KEYS.DAILY_PLANNING: {
+ handleRefresh = () => {
+ dispatch(fetchCommitments());
+ dispatch(fetchPTPDetails());
+ };
+ lastUpdatedAt = lastUpdatedAtDailyPlanning;
break;
+ }
+ case TAB_KEYS.OVERALL_METRIC: {
+ handleRefresh = () => {
+ dispatch(getOverallMetricOutput());
+ dispatch(getOverallMetricInput());
+ };
+ lastUpdatedAt = lastUpdatedAtOverallMetric;
+ break;
+ }
+ case TAB_KEYS.LIVE_LOCATION: {
+ if (!queryParams?.AGENCY) {
+ return { lastUpdatedAt: null, handleRefresh: null };
+ }
+ handleRefresh = () => {
+ if (queryParams?.AGENTID) {
+ dispatch(
+ getAgentDetails(
+ { date: queryParams?.DATE, referenceId: queryParams?.AGENTID },
+ navigate
+ )
+ );
+ } else {
+ const selectedTeamLead = queryParams[FilterTypes.TEAM_LEAD];
+ const selectedAgency = queryParams[FilterTypes.AGENCY];
+ const payload = {
+ agencyCodes: [selectedAgency],
+ teamLeadReferenceIds: selectedTeamLead ? [selectedTeamLead] : []
+ };
+ dispatch(getAgentsLocations(payload, false));
+ }
+ };
+ lastUpdatedAt = lastUpdatedAtLiveLocation;
+ break;
+ }
default:
break;
}
- };
+
+ return { lastUpdatedAt, handleRefresh };
+ }, [tabId, queryParams?.SELECTED_FEEDBACK_ID]);
+
+ const senseiTabs = useMemo(() => {
+ const tabs = [...SENSEI_TABS];
+ if (liveLocationTrackerFeatureFlag) {
+ tabs.push({
+ key: TAB_KEYS.LIVE_LOCATION,
+ value: 'Live location tracker',
+ component:
+ });
+ }
+ return tabs;
+ }, [featureFlags]);
return (
FCM Dashboard
-
- {dateFormat(new Date(lastUpdatedAt), DateFormat.LONG_DATE_FORMAT_WITH_TIME)}
-
-
-
-
- Refresh
+ {lastUpdatedAt ? (
+
+ {dateFormat(new Date(lastUpdatedAt), DateFormat.LONG_DATE_FORMAT_WITH_TIME)}
-
+ ) : null}
+ {handleRefresh ? (
+
+
+
+ Refresh
+
+
+ ) : null}
-
- {SENSEI_TABS.map(tab => {
+ {senseiTabs.map(tab => {
return (
{tab.component}
diff --git a/src/utils/ApiHelper.ts b/src/utils/ApiHelper.ts
index 50ec1033..ba1e9d3c 100644
--- a/src/utils/ApiHelper.ts
+++ b/src/utils/ApiHelper.ts
@@ -530,6 +530,8 @@ const excludedRedirectionEndPointsList = [
APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.field_governance,
APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.external_daily_planning,
APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.overall_metric,
+ APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.live_location,
+ APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.external_live_location,
APP_ROUTES_PATHS_WITH_PATH_PARAM.SENSEI.external_allocation_view
];
diff --git a/src/utils/DateHelper.ts b/src/utils/DateHelper.ts
index 0740a202..3fcbb0c5 100644
--- a/src/utils/DateHelper.ts
+++ b/src/utils/DateHelper.ts
@@ -1,4 +1,8 @@
import dayjs from 'dayjs';
+import relativeTime from 'dayjs/plugin/relativeTime';
+import isTodayDayJs from 'dayjs/plugin/isToday';
+dayjs.extend(relativeTime);
+dayjs.extend(isTodayDayJs);
export const monthNames = [
'Jan',
diff --git a/src/utils/commonUtils.ts b/src/utils/commonUtils.ts
index bc93d44c..1017f749 100644
--- a/src/utils/commonUtils.ts
+++ b/src/utils/commonUtils.ts
@@ -352,6 +352,7 @@ export function copyToClipboard(value: string) {
}
export const getNameInitials = (name: string) => {
+ if (!name) return '';
const initials = name.match(/\b\w/g) || [];
return ((initials.shift() || '') + (initials.pop() || '')).toUpperCase();
};