diff --git a/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx b/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx
deleted file mode 100644
index a5f0b0f40..000000000
--- a/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import { useMutation } from "@apollo/client";
-import React, { useState } from "react";
-import { connect } from "react-redux";
-import { createStructuredSelector } from "reselect";
-import { selectBodyshop } from "../../redux/user/user.selectors";
-import { Button, Form, Input, notification, Popover, Space } from "antd";
-import { useTranslation } from "react-i18next";
-import { UPDATE_SHOP } from "../../graphql/bodyshop.queries";
-import { logImEXEvent } from "../../firebase/firebase.utils";
-import { isFunction } from "lodash";
-
-const mapStateToProps = createStructuredSelector({
- //currentUser: selectCurrentUser
- bodyshop: selectBodyshop
-});
-const mapDispatchToProps = (dispatch) => ({
- //setUserLanguage: language => dispatch(setUserLanguage(language))
-});
-
-export function ProductionListSaveConfigButton({ columns, bodyshop, tableState, onSave }) {
- const [updateShop] = useMutation(UPDATE_SHOP);
- const [loading, setLoading] = useState(false);
- const [open, setOpen] = useState(false);
- const [form] = Form.useForm();
-
- const { t } = useTranslation();
-
- const handleSaveConfig = async (values) => {
- logImEXEvent("production_save_config");
- setLoading(true);
- const result = await updateShop({
- variables: {
- id: bodyshop.id,
- shop: {
- production_config: [
- ...bodyshop.production_config.filter((b) => b.name !== values.name),
- //Assign it to the name
- {
- name: values.name,
- columns: {
- columnKeys: columns.map((i) => {
- return { key: i.key, width: i.width };
- }),
- tableState
- }
- }
- ]
- }
- }
- });
- if (!!!result.errors) {
- notification["success"]({ message: t("bodyshop.successes.save") });
- if (onSave && isFunction(onSave)) {
- onSave();
- }
- } else {
- notification["error"]({
- message: t("bodyshop.errors.saving", {
- error: JSON.stringify(result.errors)
- })
- });
- }
- form.resetFields();
- setOpen(false);
- setLoading(false);
- };
- const popMenu = (
-
-
-
-
-
-
-
-
-
-
-
- );
-
- return (
-
-
-
- );
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(ProductionListSaveConfigButton);
diff --git a/client/src/components/production-list-table/production-list-config-manager.component.jsx b/client/src/components/production-list-table/production-list-config-manager.component.jsx
new file mode 100644
index 000000000..08d2773bc
--- /dev/null
+++ b/client/src/components/production-list-table/production-list-config-manager.component.jsx
@@ -0,0 +1,347 @@
+import { DeleteOutlined, ExclamationCircleOutlined, PlusOutlined } from "@ant-design/icons";
+import { useMutation } from "@apollo/client";
+import { Button, Form, Input, Modal, notification, Popconfirm, Popover, Select, Space } from "antd";
+import React, { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { UPDATE_ACTIVE_PROD_LIST_VIEW } from "../../graphql/associations.queries";
+import { UPDATE_SHOP } from "../../graphql/bodyshop.queries";
+import ProductionListColumns from "../production-list-columns/production-list-columns.data";
+import { useSplitTreatments } from "@splitsoftware/splitio-react";
+import { logImEXEvent } from "../../firebase/firebase.utils";
+import { isFunction } from "lodash";
+
+const { confirm } = Modal;
+
+export function ProductionListConfigManager({
+ refetch,
+ bodyshop,
+ technician,
+ currentUser,
+ state,
+ data,
+ columns,
+ setColumns,
+ setState,
+ onSave,
+ defaultView,
+ hasUnsavedChanges,
+ setHasUnsavedChanges
+}) {
+ const { t } = useTranslation();
+ const [updateDefaultProdView] = useMutation(UPDATE_ACTIVE_PROD_LIST_VIEW);
+ const [updateShop] = useMutation(UPDATE_SHOP);
+ const [loading, setLoading] = useState(false);
+ const [open, setOpen] = useState(false);
+ const [isAddingNewProfile, setIsAddingNewProfile] = useState(false);
+ const [form] = Form.useForm();
+ const [activeView, setActiveView] = useState(() => {
+ const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email);
+ return assoc && assoc.default_prod_list_view;
+ });
+
+ const defaultState = {
+ sortedInfo: {
+ columnKey: "ro_number",
+ order: null
+ },
+ filteredInfo: {}
+ };
+
+ const ensureDefaultState = (state) => {
+ return {
+ sortedInfo: state?.sortedInfo || defaultState.sortedInfo,
+ filteredInfo: state?.filteredInfo || defaultState.filteredInfo,
+ ...state
+ };
+ };
+
+ const createDefaultView = async () => {
+ const defaultConfig = {
+ name: t("production.constants.main_profile"),
+ columns: {
+ columnKeys: columns.map((i) => ({ key: i.key, width: i.width })),
+ tableState: ensureDefaultState(state)
+ }
+ };
+
+ const result = await updateShop({
+ variables: {
+ id: bodyshop.id,
+ shop: {
+ production_config: [defaultConfig]
+ }
+ }
+ });
+
+ if (!result.errors) {
+ await updateActiveProdView(t("production.constants.main_profile"));
+ setColumns(defaultConfig.columns.columnKeys);
+ setState(defaultConfig.columns.tableState);
+ notification.success({ message: t("bodyshop.successes.defaultviewcreated") });
+ } else {
+ notification.error({
+ message: t("bodyshop.errors.creatingdefaultview", {
+ error: JSON.stringify(result.errors)
+ })
+ });
+ }
+ };
+
+ const {
+ treatments: { Enhanced_Payroll }
+ } = useSplitTreatments({
+ attributes: {},
+ names: ["Enhanced_Payroll"],
+ splitKey: bodyshop.imexshopid
+ });
+
+ const updateActiveProdView = async (viewName) => {
+ const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email);
+ if (assoc) {
+ await updateDefaultProdView({
+ variables: { assocId: assoc.id, view: viewName },
+ update(cache) {
+ cache.modify({
+ id: cache.identify(bodyshop),
+ fields: {
+ associations(existingAssociations) {
+ return existingAssociations.map((a) => {
+ if (a.useremail !== currentUser.email) return a;
+ return { ...a, default_prod_list_view: viewName };
+ });
+ }
+ }
+ });
+ }
+ });
+ setActiveView(viewName);
+ setHasUnsavedChanges(false);
+ }
+ };
+
+ const handleSelect = async (value) => {
+ if (hasUnsavedChanges) {
+ confirm({
+ title: t("general.labels.unsavedchanges"),
+ icon: ,
+ content: t("general.messages.unsavedchangespopup"),
+ onOk: () => proceedWithSelect(value),
+ onCancel() {
+ // Do nothing if canceled
+ }
+ });
+ } else {
+ await proceedWithSelect(value);
+ }
+ };
+
+ const proceedWithSelect = async (value) => {
+ if (value === "add_new") {
+ setIsAddingNewProfile(true);
+ setOpen(true);
+ return;
+ }
+
+ const selectedConfig = bodyshop.production_config.find((pc) => pc.name === value);
+
+ if (selectedConfig) {
+ const newColumns = selectedConfig.columns.columnKeys.map((k) => {
+ return {
+ ...ProductionListColumns({
+ bodyshop,
+ refetch,
+ technician,
+ state: ensureDefaultState(state),
+ data: data,
+ activeStatuses: bodyshop.md_ro_statuses.active_statuses,
+ treatments: { Enhanced_Payroll }
+ }).find((e) => e.key === k.key),
+ width: k.width
+ };
+ });
+ setColumns(newColumns);
+ const newState = ensureDefaultState(selectedConfig.columns.tableState);
+ setState(newState);
+
+ await updateActiveProdView(value);
+ if (onSave && isFunction(onSave)) {
+ onSave();
+ }
+ }
+ };
+
+ const handleTrash = async (name) => {
+ if (name === t("production.constants.main_profile")) return;
+
+ const remainingConfigs = bodyshop.production_config.filter((b) => b.name !== name);
+ await updateShop({
+ variables: {
+ id: bodyshop.id,
+ shop: {
+ production_config: remainingConfigs
+ }
+ },
+ awaitRefetchQueries: true
+ });
+
+ if (name === activeView) {
+ if (remainingConfigs.length > 0) {
+ const nextConfig = remainingConfigs[0];
+ await updateActiveProdView(nextConfig.name);
+ setColumns(
+ nextConfig.columns.columnKeys.map((k) => {
+ return {
+ ...ProductionListColumns({
+ technician,
+ state: ensureDefaultState(state),
+ refetch,
+ data: data,
+ activeStatuses: bodyshop.md_ro_statuses.active_statuses,
+ treatments: { Enhanced_Payroll }
+ }).find((e) => e.key === k.key),
+ width: k.width
+ };
+ })
+ );
+ setState(ensureDefaultState(nextConfig.columns.tableState));
+ } else {
+ await updateActiveProdView(null);
+ setColumns([]);
+ setState(defaultState); // Reset to default state if no configs are left
+ }
+ }
+ };
+
+ const handleSaveConfig = async (values) => {
+ logImEXEvent("production_save_config");
+ setLoading(true);
+
+ const profileName = isAddingNewProfile ? values.name : activeView;
+
+ const result = await updateShop({
+ variables: {
+ id: bodyshop.id,
+ shop: {
+ production_config: [
+ ...bodyshop.production_config.filter((b) => b.name !== profileName),
+ {
+ name: profileName,
+ columns: {
+ columnKeys: columns.map((i) => ({ key: i.key, width: i.width })),
+ tableState: ensureDefaultState(state)
+ }
+ }
+ ]
+ }
+ }
+ });
+
+ if (!result.errors) {
+ notification.success({ message: t("bodyshop.successes.save") });
+ if (isAddingNewProfile) {
+ await updateActiveProdView(profileName);
+ }
+ if (onSave && isFunction(onSave)) {
+ onSave();
+ }
+ setHasUnsavedChanges(false);
+ } else {
+ notification.error({
+ message: t("bodyshop.errors.saving", {
+ error: JSON.stringify(result.errors)
+ })
+ });
+ }
+
+ form.resetFields();
+ setOpen(false);
+ setLoading(false);
+ setIsAddingNewProfile(false);
+ };
+
+ useEffect(() => {
+ if (!bodyshop.production_config || bodyshop.production_config.length === 0) {
+ createDefaultView().catch((e) => {
+ console.error("Something went wrong saving the production list view Config.");
+ });
+ } else {
+ setActiveView(defaultView);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [defaultView, bodyshop.production_config]);
+
+ const popMenu = (
+
+
+
+
+ )}
+
+
+
+
+
+
+ );
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/client/src/components/production-list-table/production-list-table-view-select.component.jsx b/client/src/components/production-list-table/production-list-table-view-select.component.jsx
deleted file mode 100644
index f8297fe02..000000000
--- a/client/src/components/production-list-table/production-list-table-view-select.component.jsx
+++ /dev/null
@@ -1,172 +0,0 @@
-import { DeleteOutlined } from "@ant-design/icons";
-import { useMutation } from "@apollo/client";
-import { Popconfirm, Select } from "antd";
-import React from "react";
-import { useTranslation } from "react-i18next";
-import { connect } from "react-redux";
-import { createStructuredSelector } from "reselect";
-import { UPDATE_ACTIVE_PROD_LIST_VIEW } from "../../graphql/associations.queries";
-import { UPDATE_SHOP } from "../../graphql/bodyshop.queries";
-import { selectTechnician } from "../../redux/tech/tech.selectors";
-import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
-import ProductionListColumns from "../production-list-columns/production-list-columns.data";
-import { useSplitTreatments } from "@splitsoftware/splitio-react";
-import { isFunction } from "lodash";
-
-const mapStateToProps = createStructuredSelector({
- bodyshop: selectBodyshop,
- technician: selectTechnician,
- currentUser: selectCurrentUser
-});
-
-export function ProductionListTable({
- refetch,
- bodyshop,
- technician,
- currentUser,
- state,
- data,
- setColumns,
- setState,
- onProfileChange
-}) {
- const { t } = useTranslation();
- const [updateDefaultProdView] = useMutation(UPDATE_ACTIVE_PROD_LIST_VIEW);
- const [updateShop] = useMutation(UPDATE_SHOP);
-
- const {
- treatments: { Enhanced_Payroll }
- } = useSplitTreatments({
- attributes: {},
- names: ["Enhanced_Payroll"],
- splitKey: bodyshop.imexshopid
- });
-
- const handleSelect = async (value, option) => {
- const newColumns = bodyshop.production_config
- .filter((pc) => pc.name === value)[0]
- .columns.columnKeys.map((k) => {
- return {
- ...ProductionListColumns({
- bodyshop,
- refetch,
- technician,
- state,
- data: data,
- activeStatuses: bodyshop.md_ro_statuses.active_statuses,
- treatments: { Enhanced_Payroll }
- }).find((e) => e.key === k.key),
- width: k.width
- };
- });
- setColumns(newColumns);
- const newState = bodyshop.production_config.filter((pc) => pc.name === value)[0].columns.tableState;
- setState(newState);
-
- const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email);
-
- if (assoc) {
- await updateDefaultProdView({
- variables: { assocId: assoc.id, view: value },
- update(cache) {
- cache.modify({
- id: cache.identify(bodyshop),
- fields: {
- associations(existingAssociations, { readField }) {
- return existingAssociations.map((a) => {
- if (a.useremail !== currentUser.email) return a;
- return { ...a, default_prod_list_view: value };
- });
- }
- }
- });
- }
- });
- }
-
- if (onProfileChange && isFunction(onProfileChange)) {
- onProfileChange({ value, option, newColumns, newState, assoc });
- }
- };
-
- const handleTrash = async (name) => {
- await updateShop({
- variables: {
- id: bodyshop.id,
- shop: {
- production_config: bodyshop.production_config.filter((b) => b.name !== name)
- }
- },
- awaitRefetchQueries: true
- });
-
- setColumns(
- bodyshop.production_config[0].columns.columnKeys.map((k) => {
- return {
- ...ProductionListColumns({
- technician,
- state,
- refetch,
- data: data,
- activeStatuses: bodyshop.md_ro_statuses.active_statuses,
- treatments: { Enhanced_Payroll }
- }).find((e) => e.key === k.key),
- width: k.width
- };
- })
- );
-
- setState(bodyshop.production_config[0].columns.tableState);
- };
- const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email);
-
- const defaultView = assoc && assoc.default_prod_list_view;
- return (
-
-
-
- );
-}
-
-export default connect(mapStateToProps, null)(ProductionListTable);
diff --git a/client/src/components/production-list-table/production-list-table.component.jsx b/client/src/components/production-list-table/production-list-table.component.jsx
index 6e9e1f918..728330ece 100644
--- a/client/src/components/production-list-table/production-list-table.component.jsx
+++ b/client/src/components/production-list-table/production-list-table.component.jsx
@@ -10,15 +10,14 @@ import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selecto
import ProductionListColumnsAdd from "../production-list-columns/production-list-columns.add.component";
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
import ProductionListDetail from "../production-list-detail/production-list-detail.component";
-import ProductionListSaveConfigButton from "../production-list-save-config-button/production-list-save-config-button.component";
import ProductionListPrint from "./production-list-print.component";
-import ProductionListTableViewSelect from "./production-list-table-view-select.component";
import ResizeableTitle from "./production-list-table.resizeable.component";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { SyncOutlined } from "@ant-design/icons";
import Prompt from "../../utils/prompt.js";
import _ from "lodash";
import AlertComponent from "../alert/alert.component.jsx";
+import { ProductionListConfigManager } from "./production-list-config-manager.component.jsx";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -270,23 +269,23 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici
data={data}
onColumnAdd={addColumn}
/>
- {
- setHasUnsavedChanges(false);
- }}
- />
- {
- initialStateRef.current = state;
- setHasUnsavedChanges(false);
- }}
+ setState={setState}
refetch={refetch}
data={data}
+ bodyshop={bodyshop}
+ technician={technician}
+ currentUser={currentUser}
+ defaultView={defaultView}
+ setHasUnsavedChanges={setHasUnsavedChanges}
+ hasUnsavedChanges={hasUnsavedChanges}
+ onSave={() => {
+ setHasUnsavedChanges(false);
+ initialStateRef.current = state;
+ }}
/>
setSearchText(e.target.value)}
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index c2d776fbf..79e208d91 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -271,7 +271,8 @@
},
"errors": {
"loading": "Unable to load shop details. Please call technical support.",
- "saving": "Error encountered while saving. {{message}}"
+ "saving": "Error encountered while saving. {{message}}",
+ "creatingdefaultview": "Error creating default view."
},
"fields": {
"ReceivableCustomField": "QBO Receivable Custom Field {{number}}",
@@ -699,7 +700,10 @@
"workingdays": "Working Days"
},
"successes": {
- "save": "Shop configuration saved successfully. "
+ "save": "Shop configuration saved successfully. ",
+ "unsavedchanges": "Unsaved changes will be lost. Are you sure you want to continue?",
+ "areyousure": "Are you sure you want to continue?",
+ "defaultviewcreated": "Default view created successfully."
},
"validation": {
"centermustexist": "The chosen responsibility center does not exist.",
@@ -1176,6 +1180,7 @@
"vehicle": "Vehicle"
},
"labels": {
+ "unsavedchanges": "Unsaved change.",
"actions": "Actions",
"areyousure": "Are you sure?",
"barcode": "Barcode",
@@ -2731,6 +2736,9 @@
}
},
"production": {
+ "constants":{
+ "main_profile": "Main"
+ },
"options": {
"small": "Small",
"medium": "Medium",
@@ -2834,7 +2842,8 @@
"totalhours": "Total Hrs ",
"touchtime": "T/T",
"viewname": "View Name",
- "alerts": "Alerts"
+ "alerts": "Alerts",
+ "addnewprofile": "Add New Profile"
},
"successes": {
"removed": "Job removed from production."
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index c2da8510a..76f537e61 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -271,7 +271,8 @@
},
"errors": {
"loading": "No se pueden cargar los detalles de la tienda. Por favor llame al soporte técnico.",
- "saving": ""
+ "saving": "",
+ "creatingdefaultview": ""
},
"fields": {
"ReceivableCustomField": "",
@@ -699,8 +700,11 @@
"workingdays": ""
},
"successes": {
- "save": ""
- },
+ "save": "",
+ "unsavedchanges": "",
+ "areyousure": "",
+ "defaultviewcreated": ""
+ },
"validation": {
"centermustexist": "",
"larsplit": "",
@@ -1176,6 +1180,7 @@
"vehicle": ""
},
"labels": {
+ "unsavedchanges": "",
"actions": "Comportamiento",
"areyousure": "",
"barcode": "código de barras",
@@ -2731,6 +2736,9 @@
}
},
"production": {
+ "constants":{
+ "main_profile": ""
+ },
"options": {
"small": "",
"medium": "",
@@ -2834,7 +2842,8 @@
"totalhours": "",
"touchtime": "",
"viewname": "",
- "alerts": ""
+ "alerts": "",
+ "addnewprofile": ""
},
"successes": {
"removed": ""
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 005accb1c..fccb1ce65 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -271,7 +271,8 @@
},
"errors": {
"loading": "Impossible de charger les détails de la boutique. Veuillez appeler le support technique.",
- "saving": ""
+ "saving": "",
+ "creatingdefaultview": ""
},
"fields": {
"ReceivableCustomField": "",
@@ -699,7 +700,10 @@
"workingdays": ""
},
"successes": {
- "save": ""
+ "save": "",
+ "unsavedchanges": "",
+ "areyousure": "",
+ "defaultviewcreated": ""
},
"validation": {
"centermustexist": "",
@@ -1176,6 +1180,7 @@
"vehicle": ""
},
"labels": {
+ "unsavedchanges": "",
"actions": "actes",
"areyousure": "",
"barcode": "code à barre",
@@ -2731,6 +2736,9 @@
}
},
"production": {
+ "constants":{
+ "main_profile": ""
+ },
"options": {
"small": "",
"medium": "",
@@ -2834,7 +2842,8 @@
"totalhours": "",
"touchtime": "",
"viewname": "",
- "alerts": ""
+ "alerts": "",
+ "addnewprofile": ""
},
"successes": {
"removed": ""