calls
parent
24b111d87c
commit
71d5993a0b
|
@ -16,6 +16,7 @@
|
|||
"@yudiel/react-qr-scanner": "^1.1.10",
|
||||
"antd": "^5.15.1",
|
||||
"buffer": "^6.0.3",
|
||||
"dayjs": "^1.11.10",
|
||||
"i18next": "^23.2.3",
|
||||
"i18next-browser-languagedetector": "^7.1.0",
|
||||
"i18next-http-backend": "^2.2.1",
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"@yudiel/react-qr-scanner": "^1.1.10",
|
||||
"antd": "^5.15.1",
|
||||
"buffer": "^6.0.3",
|
||||
"dayjs": "^1.11.10",
|
||||
"i18next": "^23.2.3",
|
||||
"i18next-browser-languagedetector": "^7.1.0",
|
||||
"i18next-http-backend": "^2.2.1",
|
||||
|
|
|
@ -33,7 +33,10 @@
|
|||
"copyToClipboard": "In die Zwischenablage kopieren",
|
||||
"show": "Anzeigen",
|
||||
"hide": "Verbergen",
|
||||
"reload": "Neu laden"
|
||||
"reload": "Neu laden",
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
"more": "Mehr"
|
||||
},
|
||||
"request": {
|
||||
"unknownError": {
|
||||
|
@ -290,6 +293,7 @@
|
|||
"placeholderSearch": "Suche nach",
|
||||
"buttonNew": "Neu",
|
||||
"buttonUndo": "Rückgängig machen",
|
||||
"buttonCall": "Anrufen",
|
||||
"tabs": {
|
||||
"dealInfo": "Deal-Informationen",
|
||||
"activities": "Aktivitäten",
|
||||
|
@ -341,8 +345,51 @@
|
|||
"bookedPackages": "Gebuchte Pakete",
|
||||
"assignedEmployee": "Zugewiesener Mitarbeiter"
|
||||
}
|
||||
},
|
||||
"activities": {
|
||||
"calls": {
|
||||
"title": "Anrufe",
|
||||
"deleteCallProtocolConfirm": {
|
||||
"title": "Sind Sie sicher, dass Sie das Anrufprotokoll löschen wollen?"
|
||||
}
|
||||
},
|
||||
"emails": {
|
||||
"title": "E-Mails"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"callProtocolModal": {
|
||||
"title": "Anruf protokollieren",
|
||||
"callType": "Anruftyp",
|
||||
"callTypeOptions": ["Opening-Call", "Setting-Call", "Closing-Call"],
|
||||
"callResult": [
|
||||
{
|
||||
"question": "Erreicht?",
|
||||
"result": ["Erreicht", "Nicht erreicht"]
|
||||
},
|
||||
{
|
||||
"question": "Wer hat abgenommen?",
|
||||
"yes": "Gatekeeper",
|
||||
"no": "Entscheider",
|
||||
"bothValid": true,
|
||||
"result": ["Gatekeeper", "Entscheider"]
|
||||
},
|
||||
{
|
||||
"question": "Interesse bekundet?",
|
||||
"result": ["Interesse bekundet", "Kein Interesse bekundet"]
|
||||
},
|
||||
{
|
||||
"question": "Terminiert?",
|
||||
"result": ["Terminiert", "Nicht terminiert"]
|
||||
}
|
||||
],
|
||||
"callResultText": "Anrufergebnis",
|
||||
"date": "Datum",
|
||||
"time": "Zeit",
|
||||
"telephone": "Telefon",
|
||||
"notes": "Notizen",
|
||||
"infoQrCode": "Mit dem Smartphone scannen zum Anrufen"
|
||||
}
|
||||
},
|
||||
"logCard": {
|
||||
"popover": {
|
||||
|
@ -482,7 +529,7 @@
|
|||
"displayName": "Anzeigename",
|
||||
"address": "Adresse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"userProfile": {
|
||||
|
|
|
@ -33,7 +33,10 @@
|
|||
"copyToClipboard": "Copy to clipboard",
|
||||
"show": "Show",
|
||||
"hide": "Hide",
|
||||
"reload": "Reload"
|
||||
"reload": "Reload",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"more": "More"
|
||||
},
|
||||
"request": {
|
||||
"unknownError": {
|
||||
|
@ -290,6 +293,7 @@
|
|||
"placeholderSearch": "Search for",
|
||||
"buttonNew": "New",
|
||||
"buttonUndo": "Undo",
|
||||
"buttonCall": "Call",
|
||||
"tabs": {
|
||||
"dealInfo": "Deal Info",
|
||||
"activities": "Activities",
|
||||
|
@ -341,7 +345,55 @@
|
|||
"bookedPackages": "Booked packages",
|
||||
"assignedEmployee": "Assigned employee"
|
||||
}
|
||||
},
|
||||
"activities": {
|
||||
"calls": {
|
||||
"title": "Calls",
|
||||
"deleteCallProtocolConfirm": {
|
||||
"title": "Are you sure you want to delete this call?"
|
||||
}
|
||||
},
|
||||
"emails": {
|
||||
"title": "Emails"
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"callProtocolModal": {
|
||||
"title": "Call Protocol",
|
||||
"callType": "Call Type",
|
||||
"callTypeOptions": [
|
||||
"Opening-Call",
|
||||
"Setting-Call",
|
||||
"Closing-Call"
|
||||
],
|
||||
"callResult": [
|
||||
{
|
||||
"question": "Reached?",
|
||||
"result": ["Reached", "Not reached"]
|
||||
},
|
||||
{
|
||||
"question": "Who picked up the call?",
|
||||
"yes": "Gatekeeper",
|
||||
"no": "Decision maker",
|
||||
"bothValid": true,
|
||||
"result": ["Gatekeeper", "Decision maker"]
|
||||
},
|
||||
{
|
||||
"question": "Expressed interest?",
|
||||
"result": ["Expressed interest", "No interest"]
|
||||
},
|
||||
{
|
||||
"question": "Scheduled?",
|
||||
"result": ["Scheduled", "Not scheduled"]
|
||||
}
|
||||
],
|
||||
"callResultText": "Call Result",
|
||||
"date": "Date",
|
||||
"time": "Time",
|
||||
"telephone": "Telephone",
|
||||
"notes": "Notes" ,
|
||||
"infoQrCode": "Scan with your smartphone to call"
|
||||
}
|
||||
},
|
||||
"logCard": {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { Spin } from "antd";
|
||||
import MyCenteredContainer from "../MyContainer";
|
||||
|
||||
export default function MyCenteredSpin({ fullHeight = false }) {
|
||||
return (
|
||||
<MyCenteredContainer fullHeight>
|
||||
<Spin size="large" />
|
||||
</MyCenteredContainer>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
export default function MyCenteredContainer({
|
||||
children,
|
||||
fullHeight = false,
|
||||
height = "100vh",
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignContent: "center",
|
||||
alignItems: "center",
|
||||
height: fullHeight ? height : "85.3vh",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,23 +1,25 @@
|
|||
import { Spin } from "antd";
|
||||
import { Suspense } from "react";
|
||||
import MyCenteredSpin from "../MyCenteredSpin";
|
||||
|
||||
export function MySupsenseFallback({ children }) {
|
||||
export function MySupsenseFallback({ children, spinnerCentered = true }) {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignContent: "center",
|
||||
alignItems: "center",
|
||||
textAlign: "center",
|
||||
height: "98.3vh",
|
||||
}}
|
||||
>
|
||||
<Spin size="large" />
|
||||
</div>
|
||||
spinnerCentered ? (
|
||||
<MyCenteredSpin fullHeight />
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
paddingTop: 50,
|
||||
}}
|
||||
>
|
||||
<Spin size="large" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -10,6 +10,8 @@ const preview = {
|
|||
currentDrawerCustomerRef: null,
|
||||
currentDrawerCustomerNotesRef: null,
|
||||
changedDrawerCustomerFieldsRef: null,
|
||||
currentDrawerCallProtocols: [],
|
||||
setCurrentDrawerCallProtocols: () => {},
|
||||
};
|
||||
|
||||
const CrmContext = createContext(preview);
|
||||
|
@ -31,6 +33,9 @@ export function CrmProvider({ children }) {
|
|||
// this will be used to store the updates noted and will be compared to currentDrawerCustomerRef to see if there are any changes
|
||||
const currentDrawerCustomerNotesRef = useRef(null);
|
||||
const changedDrawerCustomerFieldsRef = useRef([]);
|
||||
const [currentDrawerCallProtocols, setCurrentDrawerCallProtocols] = useState(
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<CrmContext.Provider
|
||||
|
@ -48,6 +53,8 @@ export function CrmProvider({ children }) {
|
|||
currentDrawerCustomerNotesRef,
|
||||
currentDrawerCustomerRef,
|
||||
changedDrawerCustomerFieldsRef,
|
||||
currentDrawerCallProtocols,
|
||||
setCurrentDrawerCallProtocols,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -50,6 +50,8 @@ export const ReceivedMessagesCommands = {
|
|||
AdminAreaManageLogManagerServerConnectionRemoved: 46,
|
||||
CrmCustomerUpdated: 47,
|
||||
CrmCustomerCreated: 48,
|
||||
CrmCallProtocolCreated: 49,
|
||||
CrmCallProtocolDeleted: 50,
|
||||
};
|
||||
|
||||
// commands sent to the backend server
|
||||
|
@ -1077,11 +1079,28 @@ export function handleWebSocketMessage(
|
|||
|
||||
break;
|
||||
case ReceivedMessagesCommands.CrmCustomerCreated:
|
||||
console.log("test");
|
||||
|
||||
crmContext.setCustomers((arr) => [...arr, body]);
|
||||
break;
|
||||
|
||||
case ReceivedMessagesCommands.CrmCallProtocolCreated:
|
||||
if (crmContext.currentDrawerCustomerRef.current !== null) {
|
||||
if (
|
||||
crmContext.currentDrawerCustomerRef.current.Id === body.CustomerId
|
||||
) {
|
||||
crmContext.setCurrentDrawerCallProtocols((arr) => [...arr, body]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ReceivedMessagesCommands.CrmCallProtocolDeleted:
|
||||
if (crmContext.currentDrawerCustomerRef.current !== null) {
|
||||
if (
|
||||
crmContext.currentDrawerCustomerRef.current.Id === body.CustomerId
|
||||
) {
|
||||
crmContext.setCurrentDrawerCallProtocols((arr) =>
|
||||
arr.filter((callProtocol) => callProtocol.Id !== body.Id)
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error("unknown command", cmd);
|
||||
break;
|
||||
|
|
|
@ -15,6 +15,14 @@ import {
|
|||
notification,
|
||||
Badge,
|
||||
Popover,
|
||||
DatePicker,
|
||||
TimePicker,
|
||||
QRCode,
|
||||
Card,
|
||||
Empty,
|
||||
Flex,
|
||||
Popconfirm,
|
||||
Affix,
|
||||
} from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
|
@ -30,6 +38,8 @@ import {
|
|||
PlusOutlined,
|
||||
ChromeOutlined,
|
||||
SearchOutlined,
|
||||
PhoneOutlined,
|
||||
DeleteOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { t } from "i18next";
|
||||
import { useCrmContext } from "../../Contexts/CrmContext";
|
||||
|
@ -51,6 +61,10 @@ import { frontmatterPlugin } from "@mdxeditor/editor";
|
|||
import { MyAvatar } from "../../Components/MyAvatar";
|
||||
import { useAppContext } from "../../Contexts/AppContext";
|
||||
import Highlighter from "react-highlight-words";
|
||||
import MyModal, {
|
||||
MyModalCloseCreateButtonFooter,
|
||||
} from "../../Components/MyModal";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const CRM_TYPE = {
|
||||
CUSTOMERS: 0,
|
||||
|
@ -626,7 +640,7 @@ function CustomerDrawer({ isOpen, setIsOpen, onClose, notificationApi }) {
|
|||
{
|
||||
key: "1",
|
||||
label: t("crm.tabs.activities"),
|
||||
children: <TabContentActivities />,
|
||||
children: <TabContentActivities notificationApi={notificationApi} />,
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
|
@ -653,35 +667,39 @@ function CustomerDrawer({ isOpen, setIsOpen, onClose, notificationApi }) {
|
|||
`/crm/customer/view/${crmContext.openDrawerCustomerId.current}`,
|
||||
"GET"
|
||||
).then((data) => {
|
||||
crmContext.currentDrawerCustomerRef.current = data;
|
||||
const customer = data.Customer;
|
||||
|
||||
crmContext.currentDrawerCustomerRef.current = customer;
|
||||
|
||||
crmContext.setCurrentDrawerCallProtocols(data.CallProtocols);
|
||||
|
||||
formDealInfo.setFieldsValue({
|
||||
Pipeline: data.Pipeline,
|
||||
DealPhase: data.DealPhase,
|
||||
FirstName: data.FirstName,
|
||||
LastName: data.LastName,
|
||||
Telephone: data.Telephone,
|
||||
Email: data.Email,
|
||||
Company: data.Company,
|
||||
ZipCode: data.ZipCode,
|
||||
Address: data.Address,
|
||||
City: data.City,
|
||||
Country: data.Country,
|
||||
FederalState: data.FederalState,
|
||||
Website: data.Website,
|
||||
LeadOrigin: data.LeadOrigin,
|
||||
NumberOfEmployees: data.NumberOfEmployees,
|
||||
NumberOfJobsSearchedFor: data.NumberOfJobsSearchedFor,
|
||||
JobTitle: data.JobTitle,
|
||||
NumberOfEmployeesRequired: data.NumberOfEmployeesRequired,
|
||||
HowLongHadHeBeenSearching: data.HowLongHadHeBeenSearching,
|
||||
Turnover: data.Turnover,
|
||||
DateOfcompletion: data.DateOfcompletion,
|
||||
OrderVolume: data.OrderVolume,
|
||||
NumberOfInstallments: data.NumberOfInstallments,
|
||||
AmountsOfTheInstallments: data.AmountsOfTheInstallments,
|
||||
BookedPackages: data.BookedPackages,
|
||||
AssignedEmployee: data.AssignedEmployee,
|
||||
Pipeline: customer.Pipeline,
|
||||
DealPhase: customer.DealPhase,
|
||||
FirstName: customer.FirstName,
|
||||
LastName: customer.LastName,
|
||||
Telephone: customer.Telephone,
|
||||
Email: customer.Email,
|
||||
Company: customer.Company,
|
||||
ZipCode: customer.ZipCode,
|
||||
Address: customer.Address,
|
||||
City: customer.City,
|
||||
Country: customer.Country,
|
||||
FederalState: customer.FederalState,
|
||||
Website: customer.Website,
|
||||
LeadOrigin: customer.LeadOrigin,
|
||||
NumberOfEmployees: customer.NumberOfEmployees,
|
||||
NumberOfJobsSearchedFor: customer.NumberOfJobsSearchedFor,
|
||||
JobTitle: customer.JobTitle,
|
||||
NumberOfEmployeesRequired: customer.NumberOfEmployeesRequired,
|
||||
HowLongHadHeBeenSearching: customer.HowLongHadHeBeenSearching,
|
||||
Turnover: customer.Turnover,
|
||||
DateOfcompletion: customer.DateOfcompletion,
|
||||
OrderVolume: customer.OrderVolume,
|
||||
NumberOfInstallments: customer.NumberOfInstallments,
|
||||
AmountsOfTheInstallments: customer.AmountsOfTheInstallments,
|
||||
BookedPackages: customer.BookedPackages,
|
||||
AssignedEmployee: customer.AssignedEmployee,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -721,6 +739,8 @@ function CustomerDrawer({ isOpen, setIsOpen, onClose, notificationApi }) {
|
|||
DealPhase: 1,
|
||||
AssignedEmployee: appContext.userId.current,
|
||||
});
|
||||
|
||||
crmContext.setCurrentDrawerCallProtocols([]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -839,9 +859,14 @@ function CustomerDrawer({ isOpen, setIsOpen, onClose, notificationApi }) {
|
|||
width={720}
|
||||
extra={
|
||||
<Space>
|
||||
<CallProtocolModal
|
||||
formDealInfo={formDealInfo}
|
||||
notificationApi={notificationApi}
|
||||
/>
|
||||
|
||||
<Form form={formDealInfo} layout="inline">
|
||||
<Form.Item name="AssignedEmployee">
|
||||
<Select style={{ minWidth: 100 }}>
|
||||
<Form.Item name="AssignedEmployee" style={{ margin: 0 }}>
|
||||
<Select style={{ minWidth: 120 }}>
|
||||
{appContext.users.map((user) => (
|
||||
<Select.Option key={user.Id} value={user.Id}>
|
||||
<Space>
|
||||
|
@ -1174,46 +1199,522 @@ function TabContentDealInfo({ form }) {
|
|||
);
|
||||
}
|
||||
|
||||
function TabContentActivities() {
|
||||
function TabContentActivities({ notificationApi }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<MySupsenseFallback>
|
||||
<div>Coming soon</div>
|
||||
<Tabs
|
||||
items={[
|
||||
{
|
||||
key: 0,
|
||||
label: t("crm.tabContent.activities.calls.title"),
|
||||
children: (
|
||||
<ActivityCallProtocols notificationApi={notificationApi} />
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
label: t("crm.tabContent.activities.emails.title"),
|
||||
disabled: true,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</MySupsenseFallback>
|
||||
);
|
||||
}
|
||||
|
||||
function ActivityCallProtocols({ notificationApi }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const appContext = useAppContext();
|
||||
const crmContext = useCrmContext();
|
||||
|
||||
if (crmContext.currentDrawerCallProtocols.length === 0) {
|
||||
return <Empty />;
|
||||
}
|
||||
|
||||
const sortedCallProtocols = crmContext.currentDrawerCallProtocols.sort(
|
||||
(a, b) => new Date(b.CalledAt) - new Date(a.CalledAt)
|
||||
);
|
||||
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
{sortedCallProtocols.map((item) => {
|
||||
const optionsCallResult = t("crm.callProtocolModal.callResult", {
|
||||
returnObjects: true,
|
||||
});
|
||||
const callResult = [];
|
||||
let alreadyLast = false;
|
||||
|
||||
let results = [
|
||||
"ResultReached",
|
||||
"ResultWhoPickedUp",
|
||||
"ResultExpressedInterest",
|
||||
"ResultScheduled",
|
||||
];
|
||||
|
||||
results.forEach((result, index) => {
|
||||
if (alreadyLast) return;
|
||||
|
||||
callResult.push(
|
||||
optionsCallResult[index].result[item[result] === 1 ? 0 : 1]
|
||||
);
|
||||
|
||||
if (item[result] === 0) {
|
||||
// stop if the result is not set
|
||||
alreadyLast = true;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Card key={item.Id}>
|
||||
<Card.Meta
|
||||
avatar={
|
||||
<MyAvatar
|
||||
avatarWidth={56}
|
||||
userId={item.CreatedBy}
|
||||
allUsers={appContext.users}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
<Flex justify="space-between">
|
||||
<Typography.Title level={5} style={{ marginBottom: 0 }}>{`${
|
||||
t("crm.callProtocolModal.callTypeOptions", {
|
||||
returnObjects: true,
|
||||
})[item.CallType - 1]
|
||||
}: ${callResult.join(", ")}`}</Typography.Title>
|
||||
|
||||
<Popconfirm
|
||||
placement="left"
|
||||
title={t(
|
||||
"crm.tabContent.activities.calls.deleteCallProtocolConfirm.title"
|
||||
)}
|
||||
cancelText={t("common.button.cancel")}
|
||||
okText={t("common.button.confirm")}
|
||||
onConfirm={() => {
|
||||
myFetch(`/crm/calls/delete/${item.Id}`, "DELETE")
|
||||
.then(() => {
|
||||
console.log("deleted");
|
||||
})
|
||||
.catch(() => {
|
||||
showUnkownErrorNotification(notificationApi, t);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>
|
||||
</Flex>
|
||||
}
|
||||
description={
|
||||
<Typography.Paragraph
|
||||
style={{ marginBottom: 0 }}
|
||||
ellipsis={{
|
||||
rows: 2,
|
||||
expandable: true,
|
||||
symbol: t("common.text.more"),
|
||||
}}
|
||||
>
|
||||
{`${FormatDatetime(item.CalledAt)} Uhr${
|
||||
item.Telephone !== "" ? ` - ${item.Telephone}` : ""
|
||||
} ${
|
||||
item.Notes !== ""
|
||||
? `- ${t("crm.callProtocolModal.notes")}: ${item.Notes}`
|
||||
: ""
|
||||
}`}
|
||||
</Typography.Paragraph>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
||||
function CallProtocolModal({ formDealInfo, notificationApi }) {
|
||||
const { t } = useTranslation();
|
||||
const crmContext = useCrmContext();
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [isRequesting, setIsRequesting] = useState(false);
|
||||
const [callResult, setCallResult] = useState({
|
||||
step: 0,
|
||||
answers: [],
|
||||
finished: false, // if click cancel or answered all questions
|
||||
});
|
||||
|
||||
const telephone = Form.useWatch("telephone", form);
|
||||
|
||||
const handleCancel = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
const currentDate = new Date();
|
||||
|
||||
form.setFieldsValue({
|
||||
callType: 1,
|
||||
reached: "Ja",
|
||||
date: dayjs(
|
||||
`${currentDate.getFullYear()}-${(currentDate.getMonth() + 1)
|
||||
.toString()
|
||||
.padStart(2, "0")}-${currentDate
|
||||
.getDate()
|
||||
.toString()
|
||||
.padStart(2, "0")}`,
|
||||
"YYYY-MM-DD"
|
||||
),
|
||||
time: dayjs(
|
||||
`${currentDate.getHours().toString().padStart(2, "0")}:${currentDate
|
||||
.getMinutes()
|
||||
.toString()
|
||||
.padStart(2, "0")}-${currentDate
|
||||
.getSeconds()
|
||||
.toString()
|
||||
.padStart(2, "0")}`,
|
||||
"HH:mm:ss"
|
||||
),
|
||||
telephone: formDealInfo.getFieldValue("Telephone"),
|
||||
});
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const optionsCallResult = t("crm.callProtocolModal.callResult", {
|
||||
returnObjects: true,
|
||||
});
|
||||
|
||||
const buttonCallResultTextYes =
|
||||
optionsCallResult[callResult.step].yes !== undefined
|
||||
? t("crm.callProtocolModal.callResult", {
|
||||
returnObjects: true,
|
||||
})[callResult.step].yes
|
||||
: t("common.text.yes");
|
||||
|
||||
const buttonCallResultTextNo =
|
||||
optionsCallResult[callResult.step].no !== undefined
|
||||
? t("crm.callProtocolModal.callResult", {
|
||||
returnObjects: true,
|
||||
})[callResult.step].no
|
||||
: t("common.text.no");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PhoneOutlined />}
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
{t("crm.buttonCall")}
|
||||
</Button>
|
||||
|
||||
<MyModal
|
||||
title={t("crm.callProtocolModal.title")}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
footer={
|
||||
<MyModalCloseCreateButtonFooter
|
||||
onCancel={handleCancel}
|
||||
isCreateButtonLoading={isRequesting}
|
||||
onCreate={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
setIsRequesting(true);
|
||||
|
||||
// create datetime by combining date and time
|
||||
|
||||
const date = values.date;
|
||||
const time = values.time;
|
||||
|
||||
const datetime = new Date(
|
||||
date.year(),
|
||||
date.month(),
|
||||
date.date(),
|
||||
time.hour(),
|
||||
time.minute(),
|
||||
time.second()
|
||||
);
|
||||
|
||||
myFetch("/crm/calls/create", "POST", {
|
||||
CustomerId: crmContext.openDrawerCustomerId.current,
|
||||
CallType: values.callType,
|
||||
CalledAt: datetime,
|
||||
Telephone: values.telephone,
|
||||
Notes: values.notes,
|
||||
ResultReached:
|
||||
callResult.answers[0] === null
|
||||
? 0
|
||||
: callResult.answers[0],
|
||||
ResultWhoPickedUp:
|
||||
callResult.answers[1] === null
|
||||
? 0
|
||||
: callResult.answers[1],
|
||||
ResultExpressedInterest:
|
||||
callResult.answers[2] === null
|
||||
? 0
|
||||
: callResult.answers[2],
|
||||
ResultScheduled:
|
||||
callResult.answers[3] === null
|
||||
? 0
|
||||
: callResult.answers[3],
|
||||
})
|
||||
.then(() => {
|
||||
setIsRequesting(false);
|
||||
setIsOpen(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setIsRequesting(false);
|
||||
showUnkownErrorNotification(notificationApi, t);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MySupsenseFallback spinnerCentered={false}>
|
||||
<Form form={form} layout="vertical">
|
||||
<Content>
|
||||
<Form.Item
|
||||
name="callType"
|
||||
label={t("crm.callProtocolModal.callType")}
|
||||
>
|
||||
<Select>
|
||||
{t("crm.callProtocolModal.callTypeOptions", {
|
||||
returnObjects: true,
|
||||
}).map((item, index) => (
|
||||
<Select.Option key={index} value={index + 1}>
|
||||
{item}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{callResult.finished ? (
|
||||
<Form.Item
|
||||
label={
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: 20,
|
||||
}}
|
||||
>
|
||||
<span>{t("crm.callProtocolModal.callResultText")}</span>
|
||||
<a
|
||||
onClick={() => {
|
||||
setCallResult({
|
||||
step: 0,
|
||||
answers: [],
|
||||
finished: false,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("common.button.reset")}
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 6,
|
||||
border: 1,
|
||||
paddingTop: 4,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
paddingBottom: 4,
|
||||
borderColor: "#d8d9d8",
|
||||
borderStyle: "solid",
|
||||
}}
|
||||
>
|
||||
{callResult.answers.map((item, index) => {
|
||||
return (
|
||||
<span key={index}>
|
||||
<span
|
||||
style={{
|
||||
color:
|
||||
optionsCallResult[index].bothValid !==
|
||||
undefined || item === 1
|
||||
? "green"
|
||||
: "red",
|
||||
}}
|
||||
>
|
||||
{
|
||||
optionsCallResult[index].result[
|
||||
item === 1 ? 0 : 1
|
||||
]
|
||||
}
|
||||
</span>
|
||||
{index !== callResult.answers.length - 1 && (
|
||||
<span>{" > "}</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item
|
||||
name="reached"
|
||||
label={
|
||||
t("crm.callProtocolModal.callResult", {
|
||||
returnObjects: true,
|
||||
})[callResult.step].question
|
||||
}
|
||||
style={{ width: "100%", margin: 0 }}
|
||||
>
|
||||
<Space
|
||||
styles={{ item: { width: "100%" } }}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
<Form.Item name={["erreicht", "yes"]}>
|
||||
<Button
|
||||
block
|
||||
style={{
|
||||
backgroundColor: "#f5fcdc",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (optionsCallResult.length > callResult.step + 1) {
|
||||
setCallResult({
|
||||
...callResult,
|
||||
step: callResult.step + 1,
|
||||
answers: [...callResult.answers, 1],
|
||||
});
|
||||
} else {
|
||||
setCallResult({
|
||||
...callResult,
|
||||
answers: [...callResult.answers, 1],
|
||||
finished: true,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{buttonCallResultTextYes}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item name={["erreicht", "no"]}>
|
||||
<Button
|
||||
block
|
||||
style={{ backgroundColor: "#ebebf1" }}
|
||||
onClick={() => {
|
||||
if (optionsCallResult.length > callResult.step + 1) {
|
||||
setCallResult({
|
||||
...callResult,
|
||||
step: callResult.step + 1,
|
||||
answers: [...callResult.answers, 0],
|
||||
finished:
|
||||
optionsCallResult[callResult.step].bothValid !==
|
||||
undefined
|
||||
? false
|
||||
: true,
|
||||
});
|
||||
} else {
|
||||
setCallResult({
|
||||
...callResult,
|
||||
answers: [...callResult.answers, 0],
|
||||
finished: true,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{buttonCallResultTextNo}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Content>
|
||||
|
||||
<Content>
|
||||
<Form.Item name="date" label={t("crm.callProtocolModal.date")}>
|
||||
<DatePicker style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="time"
|
||||
label="Zeit"
|
||||
rules={[
|
||||
{
|
||||
type: "object",
|
||||
required: true,
|
||||
message: "Please select time!",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TimePicker style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
</Content>
|
||||
|
||||
<Form.Item
|
||||
name="telephone"
|
||||
label={t("crm.callProtocolModal.telephone")}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="notes" label={t("crm.callProtocolModal.notes")}>
|
||||
<MyMxEditor _key="call_protocol_notes" markdown={""} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("crm.callProtocolModal.infoQrCode")}>
|
||||
{telephone !== "" ? (
|
||||
<QRCode value={`tel:${telephone}`} />
|
||||
) : (
|
||||
<Empty />
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</MySupsenseFallback>
|
||||
</MyModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TabContentNotes({ notes }) {
|
||||
const crmContext = useCrmContext();
|
||||
|
||||
return (
|
||||
<MySupsenseFallback>
|
||||
<MDXEditor
|
||||
key={crmContext.currentDrawerCustomerRef.current?.Id}
|
||||
className="mdx-editor"
|
||||
<MyMxEditor
|
||||
_key={crmContext.currentDrawerCustomerRef.current?.Id}
|
||||
markdown={notes}
|
||||
onChange={(value) => {
|
||||
crmContext.currentDrawerCustomerNotesRef.current = value;
|
||||
}}
|
||||
plugins={[
|
||||
listsPlugin(),
|
||||
quotePlugin(),
|
||||
headingsPlugin(),
|
||||
linkPlugin(),
|
||||
linkDialogPlugin(),
|
||||
tablePlugin(),
|
||||
thematicBreakPlugin(),
|
||||
frontmatterPlugin(),
|
||||
markdownShortcutPlugin(),
|
||||
toolbarPlugin({
|
||||
toolbarContents: () => (
|
||||
<>
|
||||
<UndoRedo />
|
||||
<BoldItalicUnderlineToggles />
|
||||
</>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MySupsenseFallback>
|
||||
);
|
||||
}
|
||||
|
||||
function MyMxEditor({ _key, markdown, onChange }) {
|
||||
return (
|
||||
<MDXEditor
|
||||
key={_key}
|
||||
className="mdx-editor"
|
||||
markdown={markdown}
|
||||
onChange={onChange}
|
||||
plugins={[
|
||||
listsPlugin(),
|
||||
quotePlugin(),
|
||||
headingsPlugin(),
|
||||
linkPlugin(),
|
||||
linkDialogPlugin(),
|
||||
tablePlugin(),
|
||||
thematicBreakPlugin(),
|
||||
frontmatterPlugin(),
|
||||
markdownShortcutPlugin(),
|
||||
toolbarPlugin({
|
||||
toolbarContents: () => (
|
||||
<>
|
||||
<UndoRedo />
|
||||
<BoldItalicUnderlineToggles />
|
||||
</>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue