link share

main
alex 2024-03-16 17:08:11 +01:00
parent 20b70d5d57
commit 4568663ae4
6 changed files with 273 additions and 4 deletions

View File

@ -37,7 +37,8 @@
"yes": "Ja",
"no": "Nein",
"more": "Mehr",
"pleaseInput": "Bitte eingeben"
"pleaseInput": "Bitte eingeben",
"pleaseInputValidUrl": "Bitte geben Sie eine gültige URL ein"
},
"request": {
"inputsInvalid": {
@ -363,6 +364,15 @@
},
"emails": {
"title": "E-Mails"
},
"links": {
"title": "Links",
"buttonAddLink": "Link hinzufügen",
"addLinkModal": {
"title": "Link hinzufügen",
"name": "Name",
"link": "Link"
}
}
}
},

View File

@ -37,7 +37,8 @@
"yes": "Yes",
"no": "No",
"more": "More",
"pleaseInput": "Please input"
"pleaseInput": "Please input",
"pleaseInputValidUrl": "Please input a valid URL"
},
"request": {
"inputsInvalid": {
@ -363,7 +364,15 @@
},
"emails": {
"title": "Emails"
},
"links": {
"title": "Links",
"buttonAddLink": "Add Link",
"addLinkModal": {
"title": "Add Link",
"name": "Name",
"link": "Link"
}
}
}
},

View File

@ -12,6 +12,10 @@ const preview = {
changedDrawerCustomerFieldsRef: null,
currentDrawerCallProtocols: [],
setCurrentDrawerCallProtocols: () => {},
currentDrawerActivityLinks: [],
setCurrentDrawerActivityLinks: () => {},
currentDrawerActivityLinkHistory: [],
setCurrentDrawerActivityLinkHistory: () => {},
};
const CrmContext = createContext(preview);
@ -36,6 +40,13 @@ export function CrmProvider({ children }) {
const [currentDrawerCallProtocols, setCurrentDrawerCallProtocols] = useState(
[]
);
const [currentDrawerActivityLinks, setCurrentDrawerActivityLinks] = useState(
[]
);
const [
currentDrawerActivityLinkHistory,
setCurrentDrawerActivityLinkHistory,
] = useState([]);
return (
<CrmContext.Provider
@ -55,6 +66,10 @@ export function CrmProvider({ children }) {
changedDrawerCustomerFieldsRef,
currentDrawerCallProtocols,
setCurrentDrawerCallProtocols,
currentDrawerActivityLinks,
setCurrentDrawerActivityLinks,
currentDrawerActivityLinkHistory,
setCurrentDrawerActivityLinkHistory,
}}
>
{children}

View File

@ -52,6 +52,9 @@ export const ReceivedMessagesCommands = {
CrmCustomerCreated: 48,
CrmCallProtocolCreated: 49,
CrmCallProtocolDeleted: 50,
CrmLinkCreated: 51,
CrmLinkUsed: 52,
CrmLinkDeleted: 53,
};
// commands sent to the backend server
@ -1106,6 +1109,17 @@ export function handleWebSocketMessage(
);
}
break;
case ReceivedMessagesCommands.CrmLinkCreated:
crmContext.setCurrentDrawerActivityLinks((arr) => [...arr, body]);
break;
case ReceivedMessagesCommands.CrmLinkUsed:
crmContext.setCurrentDrawerActivityLinkHistory((arr) => [...arr, body]);
break;
case ReceivedMessagesCommands.CrmLinkDeleted:
crmContext.setCurrentDrawerActivityLinks((arr) =>
arr.filter((link) => link.Id !== body)
);
break;
default:
console.error("unknown command", cmd);
break;

View File

@ -22,6 +22,7 @@ import {
Empty,
Flex,
Popconfirm,
List,
} from "antd";
import { useTranslation } from "react-i18next";
import {
@ -65,7 +66,7 @@ import MyModal, {
MyModalCloseCreateButtonFooter,
} from "../../Components/MyModal";
import dayjs from "dayjs";
import Markdown from "react-markdown";
import { MyCopyIcon } from "../../Components/MyIcon";
const CRM_TYPE = {
CUSTOMERS: 0,
@ -684,6 +685,11 @@ function CustomerDrawer({ isOpen, setIsOpen, onClose, notificationApi }) {
crmContext.currentDrawerCustomerRef.current = customer;
crmContext.setCurrentDrawerCallProtocols(data.CallProtocols);
crmContext.setCurrentDrawerActivityLinks(data.Links);
if (data.LinkHistory !== null) {
crmContext.setCurrentDrawerActivityLinkHistory(data.LinkHistory);
}
formDealInfo.setFieldsValue({
Pipeline: customer.Pipeline,
@ -1214,9 +1220,19 @@ function TabContentDealInfo({ form }) {
function TabContentActivities({ notificationApi }) {
const { t } = useTranslation();
const [activeTab, setActiveTab] = useState(0);
return (
<MySupsenseFallback>
<Tabs
activeKey={activeTab}
onChange={(activeKey) => setActiveTab(activeKey)}
tabBarExtraContent={{
right:
activeTab === 2 ? (
<AddLinkModal notificationApi={notificationApi} />
) : null,
}}
items={[
{
key: 0,
@ -1230,6 +1246,11 @@ function TabContentActivities({ notificationApi }) {
label: t("crm.tabContent.activities.emails.title"),
disabled: true,
},
{
key: 2,
label: t("crm.tabContent.activities.links.title"),
children: <ActivityLinks notificationApi={notificationApi} />,
},
]}
/>
</MySupsenseFallback>
@ -1313,6 +1334,7 @@ function ActivityCallProtocols({ notificationApi }) {
avatarWidth={56}
userId={item.CreatedBy}
allUsers={appContext.users}
tooltip
/>
}
title={
@ -1743,6 +1765,200 @@ function CallProtocolModal({ formDealInfo, notificationApi }) {
);
}
function AddLinkModal({ notificationApi }) {
const crmContext = useCrmContext();
const { t } = useTranslation();
const [isOpen, setIsOpen] = useState(false);
const [form] = Form.useForm();
const handleCancel = () => setIsOpen(false);
useEffect(() => {
if (isOpen) {
form.resetFields();
}
}, [isOpen]);
return (
<>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={() => setIsOpen(true)}
>
{t("crm.tabContent.activities.links.buttonAddLink")}
</Button>
<MyModal
title={t("crm.tabContent.activities.links.addLinkModal.title")}
isOpen={isOpen}
onCancel={handleCancel}
footer={
<MyModalCloseCreateButtonFooter
onCancel={handleCancel}
onCreate={() => {
form
.validateFields()
.then((values) => {
myFetch("/crm/links", "POST", {
CustomerId: crmContext.openDrawerCustomerId.current,
Name: values.name,
Url: values.link,
})
.then(() => {
setIsOpen(false);
form.resetFields();
})
.catch(() =>
showUnkownErrorNotification(notificationApi, t)
);
})
.catch(() => showInputsInvalidNotification(notificationApi, t));
}}
/>
}
>
<MySupsenseFallback>
<Form form={form} layout="vertical" requiredMark={false}>
<Form.Item
name="name"
label={t("crm.tabContent.activities.links.addLinkModal.name")}
rules={[
{
required: true,
message: t("common.text.pleaseInput"),
},
]}
>
<Input placeholder="Instagram" />
</Form.Item>
<Form.Item
name="link"
label={t("crm.tabContent.activities.links.addLinkModal.link")}
rules={[
{
required: true,
message: t("common.text.pleaseInput"),
},
{
type: "url",
message: t("common.text.pleaseInputValidUrl"),
},
]}
>
<Input
type="url"
placeholder="https://instagram.com/my_profile"
/>
</Form.Item>
</Form>
</MySupsenseFallback>
</MyModal>
</>
);
}
function ActivityLinks({ notificationApi }) {
const appContext = useAppContext();
const crmContext = useCrmContext();
if (crmContext.currentDrawerActivityLinks.length === 0) return <Empty />;
return (
<Space direction="vertical" style={{ width: "100%" }}>
{crmContext.currentDrawerActivityLinks.map((item) => (
<Collapse
key={item.Id}
items={[
{
key: item.Id,
label: (
<Flex justify="space-between">
<Flex vertical>
<span>{item.Name}</span>
<a href={item.Url} target="_blank" rel="noreferrer">
{item.Url}
</a>
<span>{FormatDatetime(item.CreatedAt)}</span>
</Flex>
<Space>
<span>
{
crmContext.currentDrawerActivityLinkHistory.filter(
(link) => link.LinkId === item.Id
).length
}
</span>
<MyCopyIcon
text={`${Constants.CRM_LINK_SHARE_ADDRESS}${item.Id}`}
notificationApi={notificationApi}
/>
<MyAvatar
avatarWidth={24}
userId={item.CreatedBy}
allUsers={appContext.users}
tooltip
/>
<Popconfirm
placement="left"
title={"Möchten Sie diesen Link wirklich löschen?"}
cancelText={"Abbrechen"}
okText={"Bestätigen"}
onConfirm={() => {
myFetch(`/crm/links/${item.Id}`, "DELETE").catch(() =>
showUnkownErrorNotification(notificationApi, t)
);
}}
>
<DeleteOutlined style={{ color: "darkred" }} />
</Popconfirm>
</Space>
</Flex>
),
children: (
<List
itemLayout="horizontal"
dataSource={crmContext.currentDrawerActivityLinkHistory
.filter((link) => link.LinkId === item.Id)
.sort((a, b) => new Date(b.UsedAt) - new Date(a.UsedAt))}
renderItem={(item) => (
<List.Item>
<List.Item.Meta
title={
<Flex vertical>
<span>
<span style={{ fontWeight: "bold" }}>
UserAgent:{" "}
</span>
{item.UserAgent}
</span>
<span>
<span style={{ fontWeight: "bold" }}>
Used at:{" "}
</span>
{FormatDatetime(item.UsedAt)}
</span>
</Flex>
}
/>
</List.Item>
)}
/>
),
},
]}
/>
))}
</Space>
);
}
function TabContentNotes({ notes }) {
const crmContext = useCrmContext();

View File

@ -16,6 +16,7 @@ let roboticsApiAddress = "";
var roboticsSwaggerAddress = "";
var telegramBotManagerAddress = "";
var telegramBotManagerStaticContentAddress = "";
var crmLinkShareAddress = "";
if (window.location.hostname === "localhost" && window.location.port === "") {
// for docker container testing on localhost
@ -27,6 +28,7 @@ if (window.location.hostname === "localhost" && window.location.port === "") {
roboticsSwaggerAddress = "http://localhost/rcm/swagger/index.html";
telegramBotManagerAddress = "http://localhost/tm/v1";
telegramBotManagerStaticContentAddress = "http://localhost/tm/";
crmLinkShareAddress = "http://localhost/crm/link/";
} else if (window.location.hostname === "localhost") {
// programming on localhost
apiAddress = "http://localhost:50050/v1";
@ -37,6 +39,7 @@ if (window.location.hostname === "localhost" && window.location.port === "") {
roboticsSwaggerAddress = "http://localhost:50055/swagger/index.html";
telegramBotManagerAddress = "http://localhost:50056/v1";
telegramBotManagerStaticContentAddress = "http://localhost:50056/";
crmLinkShareAddress = "http://localhost:50050/v1/crm/link/";
/*} else if (window.location.hostname === "192.168.178.93") {
apiAddress = "http://192.168.178.93:50050/v1";
staticContentAddress = "http://192.168.178.93:50050/";
@ -51,6 +54,7 @@ if (window.location.hostname === "localhost" && window.location.port === "") {
roboticsSwaggerAddress = `${window.location.protocol}${window.location.hostname}/rcm/swagger/index.html`;
telegramBotManagerAddress = `${window.location.protocol}${window.location.hostname}/tm/v1`;
telegramBotManagerStaticContentAddress = `${window.location.protocol}${window.location.hostname}/tm/`;
crmLinkShareAddress = `${window.location.protocol}${window.location.hostname}/api/v1/crm/link/`;
}
export const Constants = {
@ -76,6 +80,7 @@ export const Constants = {
ROBOTICS_SWAGGER_ADDRESS: roboticsSwaggerAddress,
TELEGRAM_BOT_MANAGER_ADDRESS: telegramBotManagerAddress,
TELEGRAM_BOT_MANAGER_CONTENT_ADDRESS: telegramBotManagerStaticContentAddress,
CRM_LINK_SHARE_ADDRESS: crmLinkShareAddress,
ROUTE_PATHS: {
EQUIPMENT_DOCUMENTATION: "/equipment-documentation",
EQUIPMENT_DOCUMENTATION_VIEW: "/equipment-documentation/",