From 831fa8e0ab6c39a2f5cb23c021459cca7fe11a47 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 6 Dec 2023 23:06:52 +0100 Subject: [PATCH] live sync --- src/App.js | 31 +-- src/Components/AppRoutes/index.js | 2 +- src/Components/SideMenu/index.js | 6 +- src/Contexts/CrmContext.js | 49 +++++ src/Contexts/WebSocketContext.js | 5 +- src/Handlers/WebSocketMessageHandler.js | 30 ++- src/Pages/Crm/index.js | 263 +++++++++++++++++++----- 7 files changed, 319 insertions(+), 67 deletions(-) create mode 100644 src/Contexts/CrmContext.js diff --git a/src/App.js b/src/App.js index f9dd862..7339394 100644 --- a/src/App.js +++ b/src/App.js @@ -15,6 +15,7 @@ import { UsersProvider } from "./Contexts/UsersContext"; import HeaderProvider from "./Contexts/HeaderContext"; import ConsolesProvider from "./Contexts/ConsolesContext"; import ScannerProvider from "./Contexts/ScannerContext"; +import { CrmProvider } from "./Contexts/CrmContext"; export default function App() { const [notificationApi, notificationContextHolder] = @@ -47,22 +48,24 @@ export default function App() { - - - - + - + isWebSocketReady={isWebSocketReady} + setIsWebSocketReady={setIsWebSocketReady} + notificationApi={notificationApi} + > + + + + + diff --git a/src/Components/AppRoutes/index.js b/src/Components/AppRoutes/index.js index 576e4a1..7fe9d2b 100644 --- a/src/Components/AppRoutes/index.js +++ b/src/Components/AppRoutes/index.js @@ -272,7 +272,7 @@ export default function AppRoutes({ userSession, setUserSession }) { Constants.PERMISSIONS.CRM.SETTER_CLOSER.VIEW ) && ( diff --git a/src/Components/SideMenu/index.js b/src/Components/SideMenu/index.js index a4e0ebb..0ad1392 100644 --- a/src/Components/SideMenu/index.js +++ b/src/Components/SideMenu/index.js @@ -178,7 +178,7 @@ export function SideMenuContent({ crmGroup.children.push({ label: t("sideMenu.crm.customers"), icon: , - key: Constants.ROUTE_PATHS.CRM + Constants.CRM_TYPE.CUSTOMERS, + key: `${Constants.ROUTE_PATHS.CRM}${Constants.CRM_TYPE.CUSTOMERS}/1`, }); } @@ -191,7 +191,7 @@ export function SideMenuContent({ crmGroup.children.push({ label: t("sideMenu.crm.dmcPipeline"), icon: , - key: Constants.ROUTE_PATHS.CRM + Constants.CRM_TYPE.DMC_PIPELINE, + key: `${Constants.ROUTE_PATHS.CRM}${Constants.CRM_TYPE.DMC_PIPELINE}/1`, }); } @@ -204,7 +204,7 @@ export function SideMenuContent({ crmGroup.children.push({ label: t("sideMenu.crm.setterCloser"), icon: , - key: Constants.ROUTE_PATHS.CRM + Constants.CRM_TYPE.SETTER_CLOSER, + key: `${Constants.ROUTE_PATHS.CRM}${Constants.CRM_TYPE.SETTER_CLOSER}/1`, }); } diff --git a/src/Contexts/CrmContext.js b/src/Contexts/CrmContext.js new file mode 100644 index 0000000..1d0291b --- /dev/null +++ b/src/Contexts/CrmContext.js @@ -0,0 +1,49 @@ +import { createContext, useContext, useRef, useState } from "react"; + +const preview = { + paginationPage: 1, + paginationPageRef: null, + totalPages: 0, + customers: [], + openDrawerCustomerId: null, + currentDrawerCustomer: null, + currentDrawerCustomerRef: null, +}; + +const CrmContext = createContext(preview); + +export const useCrmContext = () => useContext(CrmContext); + +export function CrmProvider({ children }) { + const [paginationPage, setPaginationPage] = useState(1); + const paginationPageRef = useRef(paginationPage); + const [totalPages, setTotalPages] = useState(0); + + const [customers, setCustomers] = useState([]); + // will be used to store the customer id that is currently being viewed in the drawer + const openDrawerCustomerId = useRef(null); + // will be used to store the customer object that is currently being viewed in the drawer + const [currentDrawerCustomer, setCurrentDrawerCustomer] = useState(null); + // will be set on drawer open and used to check if the customer has changed + const currentDrawerCustomerRef = useRef(null); + + return ( + + {children} + + ); +} diff --git a/src/Contexts/WebSocketContext.js b/src/Contexts/WebSocketContext.js index 19cdd5b..48733af 100644 --- a/src/Contexts/WebSocketContext.js +++ b/src/Contexts/WebSocketContext.js @@ -16,6 +16,7 @@ import { useUsersContext } from "./UsersContext"; import { useHeaderContext } from "./HeaderContext"; import { useConsolesContext } from "./ConsolesContext"; import { useScannerContext } from "./ScannerContext"; +import { useCrmContext } from "./CrmContext"; const WebSocketContext = createContext(null); @@ -44,6 +45,7 @@ export default function WebSocketProvider({ const usersContext = useUsersContext(); const consolesContext = useConsolesContext(); const scannerContext = useScannerContext(); + const crmContext = useCrmContext(); if (wsConnectionEvent === null) { wsConnectionEvent = new CustomEvent(wsConnectionCustomEventName, { @@ -103,7 +105,8 @@ export default function WebSocketProvider({ adminAreaRolesContext, usersContext, consolesContext, - scannerContext + scannerContext, + crmContext ); }; diff --git a/src/Handlers/WebSocketMessageHandler.js b/src/Handlers/WebSocketMessageHandler.js index daab2b6..b24e337 100644 --- a/src/Handlers/WebSocketMessageHandler.js +++ b/src/Handlers/WebSocketMessageHandler.js @@ -48,6 +48,7 @@ export const ReceivedMessagesCommands = { AdminAreaManageCheckedForAvailableCategories: 44, AdminAreaManageLogManagerServerConnectionAdded: 45, AdminAreaManageLogManagerServerConnectionRemoved: 46, + CrmCustomerUpdated: 47, }; // commands sent to the backend server @@ -92,7 +93,8 @@ export function handleWebSocketMessage( adminAreaRolesContext, usersContext, consolesContext, - scannerContext + scannerContext, + crmContext ) { const data = JSON.parse(event.data); @@ -1027,6 +1029,32 @@ export function handleWebSocketMessage( arr.filter((c) => c.Id !== body) ); break; + case ReceivedMessagesCommands.CrmCustomerUpdated: + console.log("CrmCustomerUpdated", body); + + console.log("current", crmContext.currentDrawerCustomerRef.current); + + // update drawer customer if it is the same customer + if (crmContext.currentDrawerCustomerRef.current !== null) { + if (crmContext.currentDrawerCustomerRef.current.Id === body.Id) { + crmContext.setCurrentDrawerCustomer(body); + } + } + + // update customers list + crmContext.setCustomers((arr) => { + const newArr = [...arr]; + + const arrIndex = arr.findIndex((customer) => customer.Id === body.Id); + + if (arrIndex !== -1) { + newArr[arrIndex] = body; + } + + return newArr; + }); + + break; default: console.error("unknown command", cmd); diff --git a/src/Pages/Crm/index.js b/src/Pages/Crm/index.js index 7a1ab79..28d8a55 100644 --- a/src/Pages/Crm/index.js +++ b/src/Pages/Crm/index.js @@ -14,15 +14,25 @@ import { Typography, } from "antd"; import { useTranslation } from "react-i18next"; -import { useParams } from "react-router-dom"; -import { AppStyle, Constants } from "../../utils"; +import { useNavigate, useParams } from "react-router-dom"; +import { + AppStyle, + Constants, + myFetch, + wsConnectionCustomEventName, +} from "../../utils"; import { useEffect, useState } from "react"; import { PlusOutlined } from "@ant-design/icons"; import { t } from "i18next"; +import PageNotFound from "../PageNotFound"; +import { useCrmContext } from "../../Contexts/CrmContext"; +import MyPagination from "../../Components/MyPagination"; export default function Crm() { const { t } = useTranslation(); - const { paramType } = useParams(); + const navigate = useNavigate(); + const crmContext = useCrmContext(); + const { paramType, paramDealPhase } = useParams(); const [isDrawerOpen, setIsDrawerOpen] = useState(false); const title = @@ -41,9 +51,7 @@ export default function Crm() { } ); - const [selectedSegmentedValue, setSelectedSegmentedValue] = useState( - segmentedOptions[0] - ); + const [selectedSegmentedValue, setSelectedSegmentedValue] = useState([]); const getTableContent = () => { return [ @@ -92,31 +100,72 @@ export default function Crm() { }; const getTableItems = () => { - return [ - { - key: "1", - id: "", - firstName: "Max", - }, - { - key: "2", - id: "", - firstName: "Peter", - }, - { - key: "3", - id: "", - firstName: "Anna", - }, - ]; + let items = []; + + crmContext.customers.forEach((item) => { + items.push({ + key: item.Id, + id: item.Id, + firstName: item.FirstName, + lastName: item.LastName, + createdAt: item.CreatedAt, + telephone: item.Telephone, + email: item.Email, + lastContact: item.LastContact, + createdBy: item.CreatedBy, + notes: item.Notes, + }); + }); + + return items; + }; + + const onPaginationChange = (page) => { + crmContext.setPaginationPage(page); + crmContext.paginationPageRef.current = page; }; useEffect(() => { if (paramType === Constants.CRM_TYPE.CUSTOMERS) return; - setSelectedSegmentedValue(segmentedOptions[0]); + setSelectedSegmentedValue(segmentedOptions[paramDealPhase - 1]); }, [paramType]); + useEffect(() => { + const customersRequest = () => + myFetch( + `/crm/pipeline/${paramType}/${paramDealPhase}?page=${crmContext.paginationPage}`, + "GET" + ).then((data) => { + if (data.Customers !== undefined) { + crmContext.setCustomers(data.Customers); + } + + if (data.TotalPages !== undefined) { + crmContext.setTotalPages(data.TotalPages); + } + }); + + customersRequest(); + + const handleCustomersRequest = () => customersRequest(); + + document.addEventListener( + wsConnectionCustomEventName, + handleCustomersRequest + ); + + return () => + document.removeEventListener( + wsConnectionCustomEventName, + handleCustomersRequest + ); + }, [paramType, paramDealPhase]); + + if (paramDealPhase > segmentedOptions.length) { + return ; + } + return ( <>
setSelectedSegmentedValue(value)} - options={segmentedOptions} + onChange={(value) => { + setSelectedSegmentedValue(value); + navigate( + `/crm/${paramType}/${segmentedOptions.indexOf(value) + 1}`, + true + ); + }} + options={segmentedOptions.map((item) => { + return { + value: item, + label: item, + }; + })} style={{ marginBottom: AppStyle.app.margin }} /> )} @@ -145,16 +205,23 @@ export default function Crm() { scroll={{ x: "max-content" }} columns={getTableContent()} dataSource={getTableItems()} - onRow={(record, rowIndex) => { + pagination={false} + onRow={(record) => { return { - onClick: (event) => { - console.log("row", record, rowIndex); + onClick: () => { setIsDrawerOpen(true); + crmContext.openDrawerCustomerId.current = record.id; }, }; }} /> + onPaginationChange(page)} + totalPages={crmContext.totalPages} + /> + setIsDrawerOpen(false)} @@ -165,6 +232,7 @@ export default function Crm() { function CustomerDrawer({ isOpen, onClose }) { const { t } = useTranslation(); + const crmContext = useCrmContext(); const tabItems = [ { @@ -184,14 +252,73 @@ function CustomerDrawer({ isOpen, onClose }) { }, ]; + useEffect(() => { + console.log("isOpen", isOpen); + + if (crmContext.openDrawerCustomerId.current === null) return; + + const customerRequest = () => { + if (!isOpen) return; + + myFetch( + `/crm/customer/view/${crmContext.openDrawerCustomerId.current}`, + "GET" + ).then((data) => { + console.log("customer", data); + crmContext.setCurrentDrawerCustomer(data); + crmContext.currentDrawerCustomerRef.current = data; + }); + }; + + if (isOpen) { + customerRequest(); + } else { + // check if something has changed + if ( + JSON.stringify(crmContext.currentDrawerCustomer) !== + JSON.stringify(crmContext.currentDrawerCustomerRef.current) + ) { + console.log("something has changed"); + + myFetch( + `/crm/customer/update/${crmContext.openDrawerCustomerId.current}`, + "POST", + crmContext.currentDrawerCustomer + ).then((data) => { + console.log("update", data); + }); + } else { + console.log("nothing has changed"); + } + + crmContext.openDrawerCustomerId.current = null; + crmContext.currentDrawerCustomer = null; + crmContext.currentDrawerCustomerRef.current = null; + } + + const handleCustomerRequest = () => customerRequest(); + + document.addEventListener( + wsConnectionCustomEventName, + handleCustomerRequest + ); + + return () => + document.removeEventListener( + wsConnectionCustomEventName, + handleCustomerRequest + ); + }, [isOpen]); + return ( + {console.log("drawer", crmContext.openDrawerCustomerId.current)} ); @@ -228,8 +355,7 @@ function CollapseContainer({ children, label }) { } function TabContentDealInfo() { - const [selectedPipeline, setSelectedPipeline] = useState(0); - const [selectedDealPhase, setSelectedDealPhase] = useState(0); + const crmContext = useCrmContext(); return ( @@ -239,21 +365,27 @@ function TabContentDealInfo() { setSelectedDealPhase(value)} + value={crmContext.currentDrawerCustomer?.DealPhase} + onChange={(value) => + crmContext.setCurrentDrawerCustomer({ + ...crmContext.currentDrawerCustomer, + DealPhase: value, + }) + } options={t( - selectedPipeline === 0 + crmContext.currentDrawerCustomer?.Pipeline === 1 ? "crm.dmcPipeline.segmentedOptions" : "crm.setterCloser.segmentedOptions", { returnObjects: true } ).map((item, index) => { return { - value: index, + value: index + 1, label: item, }; })} @@ -294,7 +431,15 @@ function TabContentDealInfo() { "crm.tabContent.dealInfo.collapseMasterDataOfContact.firstName" )} > - + + crmContext.setCurrentDrawerCustomer({ + ...crmContext.currentDrawerCustomer, + FirstName: e.target.value, + }) + } + /> - + + crmContext.setCurrentDrawerCustomer({ + ...crmContext.currentDrawerCustomer, + LastName: e.target.value, + }) + } + /> @@ -312,7 +465,15 @@ function TabContentDealInfo() { "crm.tabContent.dealInfo.collapseMasterDataOfContact.telephone" )} > - + + crmContext.setCurrentDrawerCustomer({ + ...crmContext.currentDrawerCustomer, + Telephone: e.target.value, + }) + } + /> - + + crmContext.setCurrentDrawerCustomer({ + ...crmContext.currentDrawerCustomer, + Email: e.target.value, + }) + } + />