IO-921 Multiple Prod List configurations.
This commit is contained in:
@@ -29808,6 +29808,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>selectview</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>sublets</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -29850,6 +29871,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>viewname</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { Button, notification } from "antd";
|
||||
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";
|
||||
@@ -23,21 +23,31 @@ export function ProductionListSaveConfigButton({
|
||||
}) {
|
||||
const [updateShop] = useMutation(UPDATE_SHOP);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSaveConfig = async () => {
|
||||
const handleSaveConfig = async (values) => {
|
||||
logImEXEvent("production_save_config");
|
||||
setLoading(true);
|
||||
const result = await updateShop({
|
||||
variables: {
|
||||
id: bodyshop.id,
|
||||
shop: {
|
||||
production_config: {
|
||||
columnKeys: columns.map((i) => {
|
||||
return { key: i.key, width: i.width };
|
||||
}),
|
||||
tableState,
|
||||
},
|
||||
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,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -50,13 +60,44 @@ export function ProductionListSaveConfigButton({
|
||||
}),
|
||||
});
|
||||
}
|
||||
form.resetFields();
|
||||
setVisible(false);
|
||||
setLoading(false);
|
||||
};
|
||||
const popMenu = (
|
||||
<div>
|
||||
<Form
|
||||
layout="vertical"
|
||||
form={form}
|
||||
onFinish={handleSaveConfig}
|
||||
initialValues={{ driveable: true, towin: false }}
|
||||
>
|
||||
<Form.Item
|
||||
label={t("production.labels.viewname")}
|
||||
name="name"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<Button type="danger" onClick={() => form.submit()} loading={loading}>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisible(false)}>
|
||||
{t("general.actions.close")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Button loading={loading} onClick={() => handleSaveConfig()}>
|
||||
{t("production.actions.saveconfig")}
|
||||
</Button>
|
||||
<Popover visible={visible} content={popMenu}>
|
||||
<Button loading={loading} onClick={() => setVisible(true)}>
|
||||
{t("production.actions.saveconfig")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, 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 { DeleteOutlined } from "@ant-design/icons";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
export function ProductionListTable({
|
||||
bodyshop,
|
||||
technician,
|
||||
currentUser,
|
||||
state,
|
||||
setColumns,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [updateDefaultProdView] = useMutation(UPDATE_ACTIVE_PROD_LIST_VIEW);
|
||||
const [updateShop] = useMutation(UPDATE_SHOP);
|
||||
|
||||
const handleSelect = async (value, option) => {
|
||||
setColumns(
|
||||
bodyshop.production_config
|
||||
.filter((pc) => pc.name === value)[0]
|
||||
.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({ technician, state }).find(
|
||||
(e) => e.key === k.key
|
||||
),
|
||||
width: k.width,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const assoc = bodyshop.associations.find(
|
||||
(a) => a.useremail === currentUser.email
|
||||
);
|
||||
if (assoc) {
|
||||
await updateDefaultProdView({
|
||||
variables: { assocId: assoc.id, view: value },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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 }).find(
|
||||
(e) => e.key === k.key
|
||||
),
|
||||
width: k.width,
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ width: "10rem" }}>
|
||||
<Select
|
||||
onSelect={handleSelect}
|
||||
placeholder={t("production.labels.selectview")}
|
||||
optionLabelProp="label"
|
||||
>
|
||||
{bodyshop.production_config.map((config) => (
|
||||
<Select.Option key={config.name} label={config.name}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<span style={{ flex: 1 }}>{config.name}</span>
|
||||
|
||||
<Popconfirm
|
||||
placement="right"
|
||||
title={t("general.labels.areyousure")}
|
||||
onConfirm={() => handleTrash(config.name)}
|
||||
>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(ProductionListTable);
|
||||
@@ -1,21 +1,25 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { Button, Dropdown, Input, Menu, PageHeader, Space, Table } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { Dropdown, Input, Menu, PageHeader, Space, Table } from "antd";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import ReactDragListView from "react-drag-listview";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
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 ProductionListTableViewSelect from "./production-list-table-view-select.component";
|
||||
import ResizeableTitle from "./production-list-table.resizeable.component";
|
||||
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
export function ProductionListTable({
|
||||
@@ -23,23 +27,37 @@ export function ProductionListTable({
|
||||
data,
|
||||
bodyshop,
|
||||
technician,
|
||||
refetch,
|
||||
currentUser,
|
||||
}) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
const defaultView = useMemo(() => {
|
||||
const assoc = bodyshop.associations.find(
|
||||
(a) => a.useremail === currentUser.email
|
||||
);
|
||||
return assoc && assoc.default_prod_list_view;
|
||||
}, [bodyshop.associations, currentUser.email]);
|
||||
|
||||
const [state, setState] = useState(
|
||||
(bodyshop.production_config && bodyshop.production_config.tableState) || {
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" },
|
||||
}
|
||||
(bodyshop.production_config &&
|
||||
bodyshop.production_config.find((p) => p.name === defaultView)
|
||||
?.tableState) ||
|
||||
bodyshop.production_config[0]?.tableState || {
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" },
|
||||
}
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const matchingColumnConfig = useMemo(() => {
|
||||
return bodyshop.production_config.find((p) => p.name === defaultView);
|
||||
}, [bodyshop.production_config, defaultView]);
|
||||
|
||||
const [columns, setColumns] = useState(
|
||||
(state &&
|
||||
bodyshop.production_config &&
|
||||
bodyshop.production_config.columnKeys.map((k) => {
|
||||
matchingColumnConfig &&
|
||||
matchingColumnConfig.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({ technician, state }).find(
|
||||
(e) => e.key === k.key
|
||||
@@ -126,8 +144,6 @@ export function ProductionListTable({
|
||||
.includes(searchText.toLowerCase())
|
||||
);
|
||||
|
||||
const tableTitle = () => <div style={{ display: "flex" }}></div>;
|
||||
|
||||
// const handleSelectRecord = (record) => {
|
||||
// if (selected !== record.id) {
|
||||
// setSelected(record.id);
|
||||
@@ -151,13 +167,12 @@ export function ProductionListTable({
|
||||
columns={columns}
|
||||
tableState={state}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (refetch) refetch();
|
||||
}}
|
||||
>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
|
||||
<ProductionListTableViewSelect
|
||||
state={state}
|
||||
setColumns={setColumns}
|
||||
/>
|
||||
|
||||
<Input
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
placeholder={t("general.labels.search")}
|
||||
@@ -180,7 +195,6 @@ export function ProductionListTable({
|
||||
cell: ResizeableTitle,
|
||||
},
|
||||
}}
|
||||
title={tableTitle}
|
||||
columns={columns.map((c, index) => {
|
||||
return {
|
||||
...c,
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
import { useSubscription } from "@apollo/client";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { SUBSCRIPTION_JOBS_IN_PRODUCTION } from "../../graphql/jobs.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ProductionListTable from "./production-list-table.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, null)(ProductionListTableContainer);
|
||||
|
||||
export function ProductionListTableContainer({ bodyshop, technician }) {
|
||||
export default function ProductionListTableContainer() {
|
||||
const { loading, data } = useSubscription(
|
||||
SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
||||
{}
|
||||
|
||||
@@ -21,6 +21,22 @@ export const UPDATE_ASSOCIATION = gql`
|
||||
returning {
|
||||
id
|
||||
active
|
||||
authlevel
|
||||
default_prod_list_view
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_ACTIVE_PROD_LIST_VIEW = gql`
|
||||
mutation UPDATE_ACTIVE_PROD_LIST_VIEW($assocId: uuid, $view: String) {
|
||||
update_associations(
|
||||
where: { id: { _eq: $assocId } }
|
||||
_set: { default_prod_list_view: $view }
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
default_prod_list_view
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export const QUERY_BODYSHOP = gql`
|
||||
associations {
|
||||
authlevel
|
||||
useremail
|
||||
default_prod_list_view
|
||||
user {
|
||||
authid
|
||||
email
|
||||
|
||||
@@ -1785,8 +1785,10 @@
|
||||
"note": "Production Note",
|
||||
"paintpriority": "P/P",
|
||||
"refinishhours": "R",
|
||||
"selectview": "Select a View",
|
||||
"sublets": "Sublets",
|
||||
"touchtime": "T/T"
|
||||
"touchtime": "T/T",
|
||||
"viewname": "View Name"
|
||||
},
|
||||
"successes": {
|
||||
"removed": "Job removed from production."
|
||||
|
||||
@@ -1785,8 +1785,10 @@
|
||||
"note": "",
|
||||
"paintpriority": "",
|
||||
"refinishhours": "",
|
||||
"selectview": "",
|
||||
"sublets": "",
|
||||
"touchtime": ""
|
||||
"touchtime": "",
|
||||
"viewname": ""
|
||||
},
|
||||
"successes": {
|
||||
"removed": ""
|
||||
|
||||
@@ -1785,8 +1785,10 @@
|
||||
"note": "",
|
||||
"paintpriority": "",
|
||||
"refinishhours": "",
|
||||
"selectview": "",
|
||||
"sublets": "",
|
||||
"touchtime": ""
|
||||
"touchtime": "",
|
||||
"viewname": ""
|
||||
},
|
||||
"successes": {
|
||||
"removed": ""
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."associations" DROP COLUMN "default_prod_list_view";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,6 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."associations" ADD COLUMN "default_prod_list_view" text
|
||||
NULL;
|
||||
type: run_sql
|
||||
@@ -0,0 +1,25 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- active
|
||||
- authlevel
|
||||
- id
|
||||
- shopid
|
||||
- useremail
|
||||
computed_fields: []
|
||||
filter:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,26 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- active
|
||||
- authlevel
|
||||
- default_prod_list_view
|
||||
- id
|
||||
- shopid
|
||||
- useremail
|
||||
computed_fields: []
|
||||
filter:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,23 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- active
|
||||
- authlevel
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,24 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- active
|
||||
- authlevel
|
||||
- default_prod_list_view
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -200,6 +200,7 @@ tables:
|
||||
columns:
|
||||
- active
|
||||
- authlevel
|
||||
- default_prod_list_view
|
||||
- id
|
||||
- shopid
|
||||
- useremail
|
||||
@@ -213,6 +214,7 @@ tables:
|
||||
columns:
|
||||
- active
|
||||
- authlevel
|
||||
- default_prod_list_view
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
|
||||
Reference in New Issue
Block a user