live sync

main
alex 2023-12-06 23:06:52 +01:00
parent f99a0ab573
commit 831fa8e0ab
7 changed files with 319 additions and 67 deletions

View File

@ -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() {
<UsersProvider>
<ConsolesProvider>
<ScannerProvider>
<WebSocketProvider
userSession={userSession}
setUserSession={setUserSession}
isWebSocketReady={isWebSocketReady}
setIsWebSocketReady={setIsWebSocketReady}
notificationApi={notificationApi}
>
<ReconnectingView
isWebSocketReady={isWebSocketReady}
/>
<DashboardLayout
<CrmProvider>
<WebSocketProvider
userSession={userSession}
setUserSession={setUserSession}
/>
</WebSocketProvider>
isWebSocketReady={isWebSocketReady}
setIsWebSocketReady={setIsWebSocketReady}
notificationApi={notificationApi}
>
<ReconnectingView
isWebSocketReady={isWebSocketReady}
/>
<DashboardLayout
userSession={userSession}
setUserSession={setUserSession}
/>
</WebSocketProvider>
</CrmProvider>
</ScannerProvider>
</ConsolesProvider>
</UsersProvider>

View File

@ -272,7 +272,7 @@ export default function AppRoutes({ userSession, setUserSession }) {
Constants.PERMISSIONS.CRM.SETTER_CLOSER.VIEW
) && (
<Route
path={`${Constants.ROUTE_PATHS.CRM}:paramType`}
path={`${Constants.ROUTE_PATHS.CRM}:paramType/:paramDealPhase`}
element={
<SuspenseFallback>
<Crm />

View File

@ -178,7 +178,7 @@ export function SideMenuContent({
crmGroup.children.push({
label: t("sideMenu.crm.customers"),
icon: <FileTextOutlined />,
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: <FileTextOutlined />,
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: <FileTextOutlined />,
key: Constants.ROUTE_PATHS.CRM + Constants.CRM_TYPE.SETTER_CLOSER,
key: `${Constants.ROUTE_PATHS.CRM}${Constants.CRM_TYPE.SETTER_CLOSER}/1`,
});
}

View File

@ -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 (
<CrmContext.Provider
value={{
paginationPage,
setPaginationPage,
paginationPageRef,
totalPages,
setTotalPages,
customers,
setCustomers,
openDrawerCustomerId,
currentDrawerCustomer,
setCurrentDrawerCustomer,
currentDrawerCustomerRef,
}}
>
{children}
</CrmContext.Provider>
);
}

View File

@ -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
);
};

View File

@ -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);

View File

@ -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 <PageNotFound />;
}
return (
<>
<div
@ -135,8 +184,19 @@ export default function Crm() {
{paramType !== Constants.CRM_TYPE.CUSTOMERS && (
<Segmented
value={selectedSegmentedValue}
onChange={(value) => 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;
},
};
}}
/>
<MyPagination
paginationPage={crmContext.paginationPage}
setPaginationPage={(page) => onPaginationChange(page)}
totalPages={crmContext.totalPages}
/>
<CustomerDrawer
isOpen={isDrawerOpen}
onClose={() => 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 (
<Drawer
title={"loading..."}
title={`${crmContext.currentDrawerCustomer?.FirstName} ${crmContext.currentDrawerCustomer?.LastName}`}
placement="right"
open={isOpen}
onClose={onClose}
width={720}
>
{console.log("drawer", crmContext.openDrawerCustomerId.current)}
<Tabs defaultActiveKey="0" items={tabItems} centered />
</Drawer>
);
@ -228,8 +355,7 @@ function CollapseContainer({ children, label }) {
}
function TabContentDealInfo() {
const [selectedPipeline, setSelectedPipeline] = useState(0);
const [selectedDealPhase, setSelectedDealPhase] = useState(0);
const crmContext = useCrmContext();
return (
<Space direction="vertical" style={{ display: "flex" }}>
@ -239,21 +365,27 @@ function TabContentDealInfo() {
<Content>
<Form.Item
label={t("crm.tabContent.dealInfo.collapseDealStatus.pipeline")}
initialValue={selectedPipeline}
initialValue={crmContext.currentDrawerCustomer?.Pipeline}
>
<Select
value={selectedPipeline}
value={crmContext.currentDrawerCustomer?.Pipeline}
onChange={(value) => {
setSelectedPipeline(value);
setSelectedDealPhase(0);
//setSelectedPipeline(value);
//setSelectedDealPhase(0);
crmContext.setCurrentDrawerCustomer({
...crmContext.currentDrawerCustomer,
Pipeline: value,
DealPhase: 0,
});
}}
options={[
{
value: 0,
value: 1,
label: t("crm.dmcPipeline.pageTitle"),
},
{
value: 1,
value: 2,
label: t("crm.setterCloser.pageTitle"),
},
]}
@ -262,19 +394,24 @@ function TabContentDealInfo() {
<Form.Item
label={t("crm.tabContent.dealInfo.collapseDealStatus.dealPhase")}
initialValue={selectedDealPhase}
initialValue={crmContext.currentDrawerCustomer?.DealPhase}
>
<Select
value={selectedDealPhase}
onChange={(value) => 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"
)}
>
<Input />
<Input
value={crmContext.currentDrawerCustomer?.FirstName}
onChange={(e) =>
crmContext.setCurrentDrawerCustomer({
...crmContext.currentDrawerCustomer,
FirstName: e.target.value,
})
}
/>
</Form.Item>
<Form.Item
@ -302,7 +447,15 @@ function TabContentDealInfo() {
"crm.tabContent.dealInfo.collapseMasterDataOfContact.lastName"
)}
>
<Input />
<Input
value={crmContext.currentDrawerCustomer?.LastName}
onChange={(e) =>
crmContext.setCurrentDrawerCustomer({
...crmContext.currentDrawerCustomer,
LastName: e.target.value,
})
}
/>
</Form.Item>
</Content>
@ -312,7 +465,15 @@ function TabContentDealInfo() {
"crm.tabContent.dealInfo.collapseMasterDataOfContact.telephone"
)}
>
<Input />
<Input
value={crmContext.currentDrawerCustomer?.Telephone}
onChange={(e) =>
crmContext.setCurrentDrawerCustomer({
...crmContext.currentDrawerCustomer,
Telephone: e.target.value,
})
}
/>
</Form.Item>
<Form.Item
@ -320,7 +481,15 @@ function TabContentDealInfo() {
"crm.tabContent.dealInfo.collapseMasterDataOfContact.email"
)}
>
<Input />
<Input
value={crmContext.currentDrawerCustomer?.Email}
onChange={(e) =>
crmContext.setCurrentDrawerCustomer({
...crmContext.currentDrawerCustomer,
Email: e.target.value,
})
}
/>
</Form.Item>
</Content>