filter
parent
f9867bc9eb
commit
6a1dbd0955
|
@ -21,6 +21,7 @@
|
||||||
"i18next-http-backend": "^2.2.1",
|
"i18next-http-backend": "^2.2.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-highlight-words": "^0.20.0",
|
||||||
"react-i18next": "^13.0.1",
|
"react-i18next": "^13.0.1",
|
||||||
"react-qr-scanner": "^1.0.0-alpha.11",
|
"react-qr-scanner": "^1.0.0-alpha.11",
|
||||||
"react-router-dom": "^6.10.0",
|
"react-router-dom": "^6.10.0",
|
||||||
|
@ -11173,6 +11174,11 @@
|
||||||
"he": "bin/he"
|
"he": "bin/he"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/highlight-words-core": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg=="
|
||||||
|
},
|
||||||
"node_modules/hoopy": {
|
"node_modules/hoopy": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
|
||||||
|
@ -14860,6 +14866,11 @@
|
||||||
"node": ">= 4.0.0"
|
"node": ">= 4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/memoize-one": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-QmpUu4KqDmX0plH4u+tf0riMc1KHE1+lw95cMrLlXQAFOx/xnBtwhZ52XJxd9X2O6kwKBqX32kmhbhlobD0cuw=="
|
||||||
|
},
|
||||||
"node_modules/merge-descriptors": {
|
"node_modules/merge-descriptors": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
@ -18648,6 +18659,19 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
|
||||||
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
|
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-highlight-words": {
|
||||||
|
"version": "0.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-highlight-words/-/react-highlight-words-0.20.0.tgz",
|
||||||
|
"integrity": "sha512-asCxy+jCehDVhusNmCBoxDf2mm1AJ//D+EzDx1m5K7EqsMBIHdZ5G4LdwbSEXqZq1Ros0G0UySWmAtntSph7XA==",
|
||||||
|
"dependencies": {
|
||||||
|
"highlight-words-core": "^1.2.0",
|
||||||
|
"memoize-one": "^4.0.0",
|
||||||
|
"prop-types": "^15.5.8"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^0.14.0 || ^15.0.0 || ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-hook-form": {
|
"node_modules/react-hook-form": {
|
||||||
"version": "7.48.2",
|
"version": "7.48.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.48.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.48.2.tgz",
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"i18next-http-backend": "^2.2.1",
|
"i18next-http-backend": "^2.2.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-highlight-words": "^0.20.0",
|
||||||
"react-i18next": "^13.0.1",
|
"react-i18next": "^13.0.1",
|
||||||
"react-qr-scanner": "^1.0.0-alpha.11",
|
"react-qr-scanner": "^1.0.0-alpha.11",
|
||||||
"react-router-dom": "^6.10.0",
|
"react-router-dom": "^6.10.0",
|
||||||
|
|
|
@ -12,7 +12,10 @@
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
"create": "Erstellen"
|
"create": "Erstellen",
|
||||||
|
"search": "Suchen",
|
||||||
|
"reset": "Zurücksetzen",
|
||||||
|
"filter": "Filtern"
|
||||||
},
|
},
|
||||||
"contactAdmin": "Bitte kontaktieren Sie einen Administrator",
|
"contactAdmin": "Bitte kontaktieren Sie einen Administrator",
|
||||||
"text": {
|
"text": {
|
||||||
|
@ -284,7 +287,9 @@
|
||||||
"createdBy": "Erstellt von",
|
"createdBy": "Erstellt von",
|
||||||
"notes": "Notizen"
|
"notes": "Notizen"
|
||||||
},
|
},
|
||||||
|
"placeholderSearch": "Suche nach",
|
||||||
"buttonNew": "Neu",
|
"buttonNew": "Neu",
|
||||||
|
"buttonUndo": "Rückgängig machen",
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"dealInfo": "Deal-Informationen",
|
"dealInfo": "Deal-Informationen",
|
||||||
"activities": "Aktivitäten",
|
"activities": "Aktivitäten",
|
||||||
|
|
|
@ -12,7 +12,10 @@
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"create": "Create"
|
"create": "Create",
|
||||||
|
"search": "Search",
|
||||||
|
"reset": "Reset",
|
||||||
|
"filter": "Filter"
|
||||||
},
|
},
|
||||||
"contactAdmin": "Please contact an administrator",
|
"contactAdmin": "Please contact an administrator",
|
||||||
"text": {
|
"text": {
|
||||||
|
@ -284,6 +287,7 @@
|
||||||
"createdBy": "Created by",
|
"createdBy": "Created by",
|
||||||
"notes": "Notes"
|
"notes": "Notes"
|
||||||
},
|
},
|
||||||
|
"placeholderSearch": "Search for",
|
||||||
"buttonNew": "New",
|
"buttonNew": "New",
|
||||||
"buttonUndo": "Undo",
|
"buttonUndo": "Undo",
|
||||||
"tabs": {
|
"tabs": {
|
||||||
|
|
|
@ -26,7 +26,11 @@ import {
|
||||||
wsConnectionCustomEventName,
|
wsConnectionCustomEventName,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { PlusOutlined, ChromeOutlined } from "@ant-design/icons";
|
import {
|
||||||
|
PlusOutlined,
|
||||||
|
ChromeOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { useCrmContext } from "../../Contexts/CrmContext";
|
import { useCrmContext } from "../../Contexts/CrmContext";
|
||||||
import "@mdxeditor/editor/style.css";
|
import "@mdxeditor/editor/style.css";
|
||||||
|
@ -46,6 +50,7 @@ import { tablePlugin } from "@mdxeditor/editor";
|
||||||
import { frontmatterPlugin } from "@mdxeditor/editor";
|
import { frontmatterPlugin } from "@mdxeditor/editor";
|
||||||
import { MyAvatar } from "../../Components/MyAvatar";
|
import { MyAvatar } from "../../Components/MyAvatar";
|
||||||
import { useAppContext } from "../../Contexts/AppContext";
|
import { useAppContext } from "../../Contexts/AppContext";
|
||||||
|
import Highlighter from "react-highlight-words";
|
||||||
|
|
||||||
const CRM_TYPE = {
|
const CRM_TYPE = {
|
||||||
CUSTOMERS: 0,
|
CUSTOMERS: 0,
|
||||||
|
@ -94,6 +99,10 @@ export default function CrmTest() {
|
||||||
|
|
||||||
const filterAssignedEmployeeRef = useRef([]);
|
const filterAssignedEmployeeRef = useRef([]);
|
||||||
|
|
||||||
|
const [searchText, setSearchText] = useState("");
|
||||||
|
const [searchedColumn, setSearchedColumn] = useState("");
|
||||||
|
const searchInput = useRef(null);
|
||||||
|
|
||||||
const title =
|
const title =
|
||||||
selectedSegmentedTypeValue === CRM_TYPE.CUSTOMERS
|
selectedSegmentedTypeValue === CRM_TYPE.CUSTOMERS
|
||||||
? "crm.customers.pageTitle"
|
? "crm.customers.pageTitle"
|
||||||
|
@ -129,12 +138,126 @@ export default function CrmTest() {
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSearch = (selectedKeys, confirm, dataIndex) => {
|
||||||
|
confirm();
|
||||||
|
setSearchText(selectedKeys[0]);
|
||||||
|
setSearchedColumn(dataIndex);
|
||||||
|
};
|
||||||
|
const handleReset = (clearFilters) => {
|
||||||
|
clearFilters();
|
||||||
|
setSearchText("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const getColumnSearchProps = (dataIndex) => ({
|
||||||
|
filterDropdown: ({
|
||||||
|
setSelectedKeys,
|
||||||
|
selectedKeys,
|
||||||
|
confirm,
|
||||||
|
clearFilters,
|
||||||
|
close,
|
||||||
|
}) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: 8,
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
ref={searchInput}
|
||||||
|
placeholder={`${t("crm.placeholderSearch")} ${dataIndex}`}
|
||||||
|
value={selectedKeys[0]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setSelectedKeys(e.target.value ? [e.target.value] : [])
|
||||||
|
}
|
||||||
|
onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)}
|
||||||
|
style={{
|
||||||
|
marginBottom: 8,
|
||||||
|
display: "block",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => handleSearch(selectedKeys, confirm, dataIndex)}
|
||||||
|
icon={<SearchOutlined />}
|
||||||
|
size="small"
|
||||||
|
style={{
|
||||||
|
width: 90,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common.button.search")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => clearFilters && handleReset(clearFilters)}
|
||||||
|
size="small"
|
||||||
|
style={{
|
||||||
|
width: 100,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common.button.reset")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
confirm({
|
||||||
|
closeDropdown: false,
|
||||||
|
});
|
||||||
|
setSearchText(selectedKeys[0]);
|
||||||
|
setSearchedColumn(dataIndex);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common.button.filter")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
close();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common.button.close")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
filterIcon: (filtered) => (
|
||||||
|
<SearchOutlined
|
||||||
|
style={{
|
||||||
|
color: filtered ? "#1677ff" : undefined,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
onFilter: (value, record) =>
|
||||||
|
record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()),
|
||||||
|
onFilterDropdownOpenChange: (visible) => {
|
||||||
|
if (visible) {
|
||||||
|
setTimeout(() => searchInput.current?.select(), 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: (text) =>
|
||||||
|
searchedColumn === dataIndex ? (
|
||||||
|
<Highlighter
|
||||||
|
highlightStyle={{
|
||||||
|
backgroundColor: "#ffc069",
|
||||||
|
padding: 0,
|
||||||
|
}}
|
||||||
|
searchWords={[searchText]}
|
||||||
|
autoEscape
|
||||||
|
textToHighlight={text ? text.toString() : ""}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
text
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
const getTableContent = () => {
|
const getTableContent = () => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: t("crm.table.assignedEmployee"),
|
title: t("crm.table.assignedEmployee"),
|
||||||
dataIndex: "assignedEmployee",
|
dataIndex: "assignedEmployee",
|
||||||
key: "assignedEmployee",
|
key: "assignedEmployee",
|
||||||
|
sorter: (a, b) => sorter(a._assignedEmployee, b._assignedEmployee),
|
||||||
filters: filterAssignedEmployeeRef.current.map((item) => {
|
filters: filterAssignedEmployeeRef.current.map((item) => {
|
||||||
return {
|
return {
|
||||||
text:
|
text:
|
||||||
|
@ -150,24 +273,28 @@ export default function CrmTest() {
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
onFilter: (value, record) => record._assignedEmployee === value,
|
onFilter: (value, record) => record._assignedEmployee === value,
|
||||||
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("crm.table.firstName"),
|
title: t("crm.table.firstName"),
|
||||||
dataIndex: "firstName",
|
dataIndex: "firstName",
|
||||||
key: "firstName",
|
key: "firstName",
|
||||||
sorter: (a, b) => sorter(a.firstName, b.firstName),
|
sorter: (a, b) => sorter(a.firstName, b.firstName),
|
||||||
|
...getColumnSearchProps("firstName"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("crm.table.lastName"),
|
title: t("crm.table.lastName"),
|
||||||
dataIndex: "lastName",
|
dataIndex: "lastName",
|
||||||
key: "lastName",
|
key: "lastName",
|
||||||
sorter: (a, b) => sorter(a.lastName, b.lastName),
|
sorter: (a, b) => sorter(a.lastName, b.lastName),
|
||||||
|
...getColumnSearchProps("lastName"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("crm.table.company"),
|
title: t("crm.table.company"),
|
||||||
dataIndex: "company",
|
dataIndex: "company",
|
||||||
key: "company",
|
key: "company",
|
||||||
sorter: (a, b) => sorter(a.company, b.company),
|
sorter: (a, b) => sorter(a.company, b.company),
|
||||||
|
...getColumnSearchProps("company"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("crm.table.createdAt"),
|
title: t("crm.table.createdAt"),
|
||||||
|
@ -188,12 +315,14 @@ export default function CrmTest() {
|
||||||
dataIndex: "telephone",
|
dataIndex: "telephone",
|
||||||
key: "telephone",
|
key: "telephone",
|
||||||
sorter: (a, b) => sorter(a.telephone, b.telephone),
|
sorter: (a, b) => sorter(a.telephone, b.telephone),
|
||||||
|
...getColumnSearchProps("telephone"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("crm.table.email"),
|
title: t("crm.table.email"),
|
||||||
dataIndex: "email",
|
dataIndex: "email",
|
||||||
key: "email",
|
key: "email",
|
||||||
sorter: (a, b) => sorter(a.email, b.email),
|
sorter: (a, b) => sorter(a.email, b.email),
|
||||||
|
...getColumnSearchProps("email"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("crm.table.lastContact"),
|
title: t("crm.table.lastContact"),
|
||||||
|
@ -410,11 +539,13 @@ export default function CrmTest() {
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
|
marginTop: 6,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography.Title level={4}>{t(title)}</Typography.Title>
|
<Typography.Title level={4}>{t(title)}</Typography.Title>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
type="primary"
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
crmContext.openDrawerCustomerId.current = "new";
|
crmContext.openDrawerCustomerId.current = "new";
|
||||||
|
@ -710,7 +841,7 @@ function CustomerDrawer({ isOpen, setIsOpen, onClose, notificationApi }) {
|
||||||
<Space>
|
<Space>
|
||||||
<Form form={formDealInfo} layout="inline">
|
<Form form={formDealInfo} layout="inline">
|
||||||
<Form.Item name="AssignedEmployee">
|
<Form.Item name="AssignedEmployee">
|
||||||
<Select>
|
<Select style={{ minWidth: 100 }}>
|
||||||
{appContext.users.map((user) => (
|
{appContext.users.map((user) => (
|
||||||
<Select.Option key={user.Id} value={user.Id}>
|
<Select.Option key={user.Id} value={user.Id}>
|
||||||
<Space>
|
<Space>
|
||||||
|
@ -783,7 +914,7 @@ function TabContentDealInfo({ form }) {
|
||||||
|
|
||||||
const FormItem = ({ name, label }) => (
|
const FormItem = ({ name, label }) => (
|
||||||
<Form.Item name={name} label={typeof label === "string" ? t(label) : label}>
|
<Form.Item name={name} label={typeof label === "string" ? t(label) : label}>
|
||||||
<Input />
|
<Input maxLength={250} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue