172 lines
5.4 KiB
JavaScript
172 lines
5.4 KiB
JavaScript
import Icon, { SyncOutlined } from "@ant-design/icons";
|
|
import { isEmpty, cloneDeep } from "lodash";
|
|
import { useMutation, useQuery } from "@apollo/client";
|
|
import { Button, Dropdown, Space } from "antd";
|
|
import { PageHeader } from "@ant-design/pro-layout";
|
|
import { useMemo, useState } from "react";
|
|
import { Responsive, WidthProvider } from "react-grid-layout";
|
|
import { useTranslation } from "react-i18next";
|
|
import { MdClose } from "react-icons/md";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
|
import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
|
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
|
import AlertComponent from "../alert/alert.component";
|
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
|
import { GenerateDashboardData } from "./dashboard-grid.utils";
|
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
|
import componentList from "./componentList.js";
|
|
import createDashboardQuery from "./createDashboardQuery.js";
|
|
|
|
import "./dashboard-grid.styles.scss";
|
|
|
|
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
currentUser: selectCurrentUser,
|
|
bodyshop: selectBodyshop
|
|
});
|
|
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
|
});
|
|
|
|
export function DashboardGridComponent({ currentUser, bodyshop }) {
|
|
const { t } = useTranslation();
|
|
const [state, setState] = useState({
|
|
...(bodyshop.associations[0].user.dashboardlayout
|
|
? bodyshop.associations[0].user.dashboardlayout
|
|
: { items: [], layout: {}, layouts: [] })
|
|
});
|
|
const notification = useNotification();
|
|
|
|
const { loading, error, data, refetch } = useQuery(createDashboardQuery(state), {
|
|
fetchPolicy: "network-only",
|
|
nextFetchPolicy: "network-only"
|
|
});
|
|
|
|
const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
|
|
|
|
const handleLayoutChange = async (layout, layouts) => {
|
|
logImEXEvent("dashboard_change_layout");
|
|
|
|
setState({ ...state, layout, layouts });
|
|
|
|
const result = await updateLayout({
|
|
variables: {
|
|
email: currentUser.email,
|
|
layout: { ...state, layout, layouts }
|
|
}
|
|
});
|
|
|
|
if (!isEmpty(result?.errors)) {
|
|
notification.error({
|
|
message: t("dashboard.errors.updatinglayout", {
|
|
message: JSON.stringify(result.errors)
|
|
})
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleRemoveComponent = (key) => {
|
|
logImEXEvent("dashboard_remove_component", { name: key });
|
|
const idxToRemove = state.items.findIndex((i) => i.i === key);
|
|
|
|
const items = cloneDeep(state.items);
|
|
|
|
items.splice(idxToRemove, 1);
|
|
setState({ ...state, items });
|
|
};
|
|
|
|
const handleAddComponent = (e) => {
|
|
logImEXEvent("dashboard_add_component", { name: e });
|
|
setState({
|
|
...state,
|
|
items: [
|
|
...state.items,
|
|
{
|
|
i: e.key,
|
|
x: (state.items.length * 2) % (state.cols || 12),
|
|
y: 99, // puts it at the bottom
|
|
w: componentList[e.key].w || 2,
|
|
h: componentList[e.key].h || 2
|
|
}
|
|
]
|
|
});
|
|
};
|
|
|
|
const dashboardData = useMemo(() => GenerateDashboardData(data), [data]);
|
|
|
|
const existingLayoutKeys = state.items.map((i) => i.i);
|
|
|
|
const menuItems = Object.keys(componentList).map((key) => ({
|
|
key: key,
|
|
label: componentList[key].label,
|
|
value: key,
|
|
disabled: existingLayoutKeys.includes(key)
|
|
}));
|
|
|
|
const menu = { items: menuItems, onClick: handleAddComponent };
|
|
|
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
|
|
|
return (
|
|
<div>
|
|
<PageHeader
|
|
extra={
|
|
<Space>
|
|
<Button onClick={() => refetch()}>
|
|
<SyncOutlined />
|
|
</Button>
|
|
<Dropdown menu={menu} trigger={["click"]}>
|
|
<Button>{t("dashboard.actions.addcomponent")}</Button>
|
|
</Dropdown>
|
|
</Space>
|
|
}
|
|
/>
|
|
|
|
<ResponsiveReactGridLayout
|
|
className="layout"
|
|
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
|
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
|
width="100%"
|
|
layouts={state.layouts}
|
|
onLayoutChange={handleLayoutChange}
|
|
>
|
|
{state.items.map((item, index) => {
|
|
const TheComponent = componentList[item.i].component;
|
|
return (
|
|
<div
|
|
key={item.i}
|
|
data-grid={{
|
|
...item,
|
|
minH: componentList[item.i].minH || 1,
|
|
minW: componentList[item.i].minW || 1
|
|
}}
|
|
>
|
|
<LoadingSkeleton loading={loading}>
|
|
<Icon
|
|
component={MdClose}
|
|
key={item.i}
|
|
style={{
|
|
position: "absolute",
|
|
zIndex: "2",
|
|
right: ".25rem",
|
|
top: ".25rem",
|
|
cursor: "pointer"
|
|
}}
|
|
onClick={() => handleRemoveComponent(item.i)}
|
|
/>
|
|
<TheComponent className="dashboard-card" bodyshop={bodyshop} data={dashboardData} />
|
|
</LoadingSkeleton>
|
|
</div>
|
|
);
|
|
})}
|
|
</ResponsiveReactGridLayout>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(DashboardGridComponent);
|