Added shop settings form to update groups.
This commit is contained in:
@@ -19,8 +19,6 @@ if (isDev) {
|
||||
REACT_DEVELOPER_TOOLS = devTools.REACT_DEVELOPER_TOOLS;
|
||||
}
|
||||
|
||||
console.log(`${__dirname}/preload.js`);
|
||||
|
||||
let mainWindow;
|
||||
let tray = null;
|
||||
function createWindow() {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- targets
|
||||
filter:
|
||||
associations:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,23 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- accepted_ins_co
|
||||
- shopname
|
||||
- targets
|
||||
filter:
|
||||
associations:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -57,6 +57,8 @@ tables:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- accepted_ins_co
|
||||
- shopname
|
||||
- targets
|
||||
filter:
|
||||
associations:
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import { UpOutlined, DownOutlined } from "@ant-design/icons";
|
||||
|
||||
export default function FormListMoveArrows({ move, index, total }) {
|
||||
const upDisabled = index === 0;
|
||||
const downDisabled = index === total - 1;
|
||||
|
||||
const handleUp = () => {
|
||||
move(index, index - 1);
|
||||
};
|
||||
|
||||
const handleDown = () => {
|
||||
move(index, index - 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<UpOutlined disabled={upDisabled} onClick={handleUp} />
|
||||
<DownOutlined disabled={downDisabled} onClick={handleDown} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import React from "react";
|
||||
import { Row, Col, Typography } from "antd";
|
||||
import "./layout-form-row.styles.scss";
|
||||
|
||||
export default function LayoutFormRow({
|
||||
header,
|
||||
children,
|
||||
grow = false,
|
||||
...restProps
|
||||
}) {
|
||||
if (!!!children.length) {
|
||||
//We have only one element. It's going to get the whole thing.
|
||||
return (
|
||||
<div className="imex-form-row">
|
||||
{header ? (
|
||||
<Typography.Title level={4}>{header}</Typography.Title>
|
||||
) : null}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const rowGutter = { gutter: [16, 16] };
|
||||
|
||||
const colSpan = (spanOverride) => {
|
||||
if (spanOverride) return { span: spanOverride };
|
||||
return {
|
||||
xs: {
|
||||
span: !grow ? 24 : Math.max(12, 24 / children.length),
|
||||
},
|
||||
sm: {
|
||||
span: !grow ? 12 : Math.max(12, 24 / children.length),
|
||||
},
|
||||
md: {
|
||||
span: !grow ? 8 : Math.max(8, 24 / children.length),
|
||||
},
|
||||
lg: {
|
||||
span: !grow ? 6 : Math.max(6, 24 / children.length),
|
||||
},
|
||||
xl: {
|
||||
span: !grow ? 4 : Math.max(4, 24 / children.length),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="imex-form-row" {...restProps}>
|
||||
{header ? <Typography.Title level={4}>{header}</Typography.Title> : null}
|
||||
<Row {...rowGutter}>
|
||||
{children.map(
|
||||
(c, idx) =>
|
||||
c && (
|
||||
<Col key={idx} {...colSpan(c && c.props && c.props.span)}>
|
||||
{c}
|
||||
</Col>
|
||||
)
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//Override Antd Row margin to save space on forms.
|
||||
.imex-form-row {
|
||||
.ant-row {
|
||||
margin-bottom: 0rem;
|
||||
|
||||
.ant-form-item-label {
|
||||
padding: 0rem;
|
||||
}
|
||||
|
||||
label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
padding: 0rem;
|
||||
margin: 0rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export function PriceDiffPcFormatterAtom({
|
||||
v_age,
|
||||
}) {
|
||||
const metTarget = useMemo(() => {
|
||||
const targetsForGroup = bodyshop.targets[group];
|
||||
const targetsForGroup = bodyshop.targets.filter((t) => t.group === group);
|
||||
if (!targetsForGroup) return 0;
|
||||
const targetPc = targetsForGroup.filter(
|
||||
(t) => t.ageGte <= v_age && (t.ageLt ? t.ageLt > v_age : true)
|
||||
|
||||
@@ -13,7 +13,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
export function PriceDiffPcFormatterAtom({ bodyshop, group, v_age }) {
|
||||
const metTarget = useMemo(() => {
|
||||
const targetsForGroup = bodyshop.targets[group];
|
||||
const targetsForGroup = bodyshop.targets.filter((t) => t.group === group);
|
||||
if (!targetsForGroup) return 0;
|
||||
const targetPc = targetsForGroup.filter(
|
||||
(t) => t.ageGte <= v_age && (t.ageLt ? t.ageLt > v_age : true)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import { Button, Input, Form, Select, InputNumber } from "antd";
|
||||
import FormListMoveArrows from "../../atoms/form-list-move-arrows/form-list-move-arrows.atom";
|
||||
import React from "react";
|
||||
import LayoutFormRow from "../../atoms/layout-form-row/layout-form-row.atom";
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
|
||||
export default function ShopSettingsFormMolecule({ form, saveLoading }) {
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
type="primary"
|
||||
loading={saveLoading}
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Form.Item
|
||||
label="Shop Name"
|
||||
name="shopname"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="accepted_ins_co"
|
||||
label="Accepted Insurance Company Names (must be exactly as in estimating system)"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
|
||||
type: "array",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List name={["targets"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
label="Group"
|
||||
key={`${index}group`}
|
||||
name={[field.name, "group"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Age >="
|
||||
key={`${index}ageGte`}
|
||||
name={[field.name, "ageGte"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Age <"
|
||||
key={`${index}ageLt`}
|
||||
name={[field.name, "ageLt"]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Target (as decimal)"
|
||||
key={`${index}target`}
|
||||
name={[field.name, "target"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<div>
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
<FormListMoveArrows
|
||||
move={move}
|
||||
index={index}
|
||||
total={fields.length}
|
||||
/>
|
||||
</div>
|
||||
</LayoutFormRow>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import { Form, notification, Skeleton } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../../graphql/bodyshop.queries";
|
||||
import { setBodyshop } from "../../../redux/user/user.actions";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import ShopSettingsFormMolecule from "../../molecules/shop-settings-form/shop-settings-form.molecule";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
setBodyshop: (shop) => dispatch(setBodyshop(shop)),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ShopSettingsOrganism);
|
||||
|
||||
export function ShopSettingsOrganism({ setBodyshop }) {
|
||||
const { loading, error, data } = useQuery(QUERY_BODYSHOP);
|
||||
const [form] = Form.useForm();
|
||||
const [updateBodyshop] = useMutation(UPDATE_SHOP);
|
||||
const [saveLoading, setSaveLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) form.resetFields();
|
||||
}, [form, data]);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setSaveLoading(true);
|
||||
|
||||
const result = await updateBodyshop({
|
||||
variables: { id: data.bodyshops[0].id, shop: values },
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({ message: "Settings saved successfully." });
|
||||
// console.log("r", result.data.update_bodyshops.returning[0]);
|
||||
setBodyshop(result.data.update_bodyshops.returning[0]);
|
||||
form.resetFields();
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "Error while saving settings " + error,
|
||||
});
|
||||
}
|
||||
setSaveLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
autoComplete="new-password"
|
||||
onFinish={handleFinish}
|
||||
initialValues={data ? data.bodyshops[0] : null}
|
||||
>
|
||||
{loading && <Skeleton />}
|
||||
{error && <ErrorResultAtom title="Error displaying job data." />}
|
||||
<ShopSettingsFormMolecule
|
||||
loading={loading}
|
||||
form={form}
|
||||
saveLoading={saveLoading}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import React from "react";
|
||||
import FilePathsListOrganism from "../../organisms/filepaths-list/filepaths-list.organism";
|
||||
import ShopSettingsOrganism from "../../organisms/shop-settings/shop-settings.organism";
|
||||
import WatcherManagerOrganism from "../../organisms/watcher-manager/watcher-manager.organism";
|
||||
export default function SettingsPage() {
|
||||
return (
|
||||
<div>
|
||||
<FilePathsListOrganism />
|
||||
<WatcherManagerOrganism />
|
||||
<ShopSettingsOrganism />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,3 +9,15 @@ export const QUERY_BODYSHOP = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const UPDATE_SHOP = gql`
|
||||
mutation UPDATE_SHOP($id: uuid, $shop: bodyshops_set_input!) {
|
||||
update_bodyshops(where: { id: { _eq: $id } }, _set: $shop) {
|
||||
returning {
|
||||
id
|
||||
shopname
|
||||
targets
|
||||
accepted_ins_co
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user