IO-921 Multiple Prod List configurations.

This commit is contained in:
Patrick Fic
2021-05-03 16:57:03 -07:00
parent 49603e5951
commit 4e78f8223f
17 changed files with 387 additions and 48 deletions

View File

@@ -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>

View File

@@ -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(

View File

@@ -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);

View File

@@ -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,

View File

@@ -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,
{}

View File

@@ -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
}
}
}

View File

@@ -14,6 +14,7 @@ export const QUERY_BODYSHOP = gql`
associations {
authlevel
useremail
default_prod_list_view
user {
authid
email

View File

@@ -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."

View File

@@ -1785,8 +1785,10 @@
"note": "",
"paintpriority": "",
"refinishhours": "",
"selectview": "",
"sublets": "",
"touchtime": ""
"touchtime": "",
"viewname": ""
},
"successes": {
"removed": ""

View File

@@ -1785,8 +1785,10 @@
"note": "",
"paintpriority": "",
"refinishhours": "",
"selectview": "",
"sublets": "",
"touchtime": ""
"touchtime": "",
"viewname": ""
},
"successes": {
"removed": ""

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."associations" DROP COLUMN "default_prod_list_view";
type: run_sql

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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: