Merged in dev-patrick (pull request #8)
Merge all WIP dev changes to Dev Branch.
This commit is contained in:
17
.vscode/launch.json
vendored
17
.vscode/launch.json
vendored
@@ -1,19 +1,20 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to Chrome",
|
||||
"port": 9222,
|
||||
"request": "attach",
|
||||
"type": "pwa-chrome",
|
||||
"webRoot": "${workspaceFolder}/client/src"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Chrome",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceRoot}/src"
|
||||
},
|
||||
{
|
||||
"name": "Yarn Dev Server",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "yarn",
|
||||
"runtimeArgs": ["dev"]
|
||||
"webRoot": "${workspaceRoot}/client/src"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
React App:
|
||||
|
||||
Yarn Dependency Management:
|
||||
To force upgrades for some packages: yarn upgrade-interactive --latest
|
||||
To force upgrades for some packages:
|
||||
yarn upgrade-interactive --latest
|
||||
|
||||
GraphQL API:
|
||||
Hasura is hosted on another dyno. Several environmental variables are required, including disabling the console.
|
||||
@@ -19,4 +20,6 @@ NGROK TEsting:
|
||||
|
||||
|
||||
Finding deadfiles - run from client directory
|
||||
npx deadfile ./src/index.js --exclude build templates
|
||||
npx deadfile ./src/index.js --exclude build templates
|
||||
|
||||
cd client && yarn build && cd build && scp -r ** imex@prod-tor1.imex.online:~/bodyshop/client/build && cd .. &&cd ..
|
||||
@@ -3,21 +3,23 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.0.2",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"@apollo/client": "^3.1.2",
|
||||
"@testing-library/jest-dom": "^5.11.2",
|
||||
"@testing-library/react": "^10.4.8",
|
||||
"@testing-library/user-event": "^12.1.0",
|
||||
"@types/prop-types": "^15.7.3",
|
||||
"apollo-boost": "^0.4.9",
|
||||
"apollo-link-context": "^1.0.20",
|
||||
"apollo-link-logger": "^1.2.3",
|
||||
"dotenv": "^8.2.0",
|
||||
"firebase": "^7.17.0",
|
||||
"firebase": "^7.17.1",
|
||||
"graphql": "^15.3.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"ra-data-hasura-graphql": "^0.1.12",
|
||||
"react": "^16.13.1",
|
||||
"react-admin": "^3.7.1",
|
||||
"react-admin": "^3.7.2",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-icons": "^3.10.0",
|
||||
"react-scripts": "3.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";
|
||||
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
|
||||
import { ApolloLink } from "apollo-boost";
|
||||
import { setContext } from "apollo-link-context";
|
||||
import { HttpLink } from "apollo-link-http";
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Resource,
|
||||
ShowGuesser,
|
||||
} from "react-admin";
|
||||
import { FaFileInvoiceDollar } from "react-icons/fa";
|
||||
import { auth } from "../../firebase/admin-firebase-utils";
|
||||
import authProvider from "../auth-provider/auth-provider";
|
||||
import JoblinesCreate from "../joblines/joblines.create";
|
||||
@@ -91,6 +92,7 @@ class AdminRoot extends Component {
|
||||
<ApolloProvider client={client}>
|
||||
<Admin dataProvider={dataProvider} authProvider={authProvider}>
|
||||
<Resource
|
||||
icon={FaFileInvoiceDollar}
|
||||
name="jobs"
|
||||
list={JobsList}
|
||||
edit={JobsEdit}
|
||||
|
||||
@@ -9,7 +9,6 @@ const authProvider = {
|
||||
password
|
||||
);
|
||||
const token = await user.getIdToken(true);
|
||||
console.log("token", token);
|
||||
localStorage.setItem("token", token);
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
@@ -23,23 +22,17 @@ const authProvider = {
|
||||
return Promise.resolve();
|
||||
},
|
||||
checkAuth: async (params) => {
|
||||
console.log("Check Auth", params);
|
||||
const user = await getCurrentUser();
|
||||
if (!!user) {
|
||||
console.log("AuthProvider => checkAuth => Authorized");
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
console.log("AuthProvider => checkAuth => Unauthorized");
|
||||
|
||||
return Promise.reject();
|
||||
}
|
||||
},
|
||||
checkError: (error) => {
|
||||
console.log("Check error");
|
||||
return Promise.resolve();
|
||||
},
|
||||
getPermissions: (params) => {
|
||||
console.log("get permissions", params);
|
||||
return Promise.resolve();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Edit,
|
||||
EmailField,
|
||||
DateTimeInput,
|
||||
DateField,
|
||||
NumberInput,
|
||||
BooleanInput,
|
||||
DateField,
|
||||
Edit,
|
||||
NumberInput,
|
||||
SimpleForm,
|
||||
TextInput,
|
||||
} from "react-admin";
|
||||
import { number } from "prop-types";
|
||||
|
||||
const JoblinesEdit = (props) => (
|
||||
<Edit {...props}>
|
||||
|
||||
@@ -1,53 +1,150 @@
|
||||
import React from "react";
|
||||
import { Edit, SimpleForm, TextInput } from "react-admin";
|
||||
//@ts-ignore
|
||||
import {
|
||||
AutocompleteInput,
|
||||
Edit,
|
||||
FormTab,
|
||||
NumberInput,
|
||||
ReferenceInput,
|
||||
SelectInput,
|
||||
TabbedForm,
|
||||
TextInput,
|
||||
} from "react-admin";
|
||||
|
||||
const JobsEdit = (props) => (
|
||||
<Edit {...props}>
|
||||
<SimpleForm margin="normal" variant="standard">
|
||||
<div
|
||||
style={{
|
||||
columns: "3 auto",
|
||||
// display: "flex",
|
||||
width: "100%",
|
||||
// justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<TextInput fullWidth source="id" />
|
||||
<TextInput fullWidth source="created_at" />
|
||||
<TextInput fullWidth source="updated_at" />
|
||||
<TabbedForm margin="normal" variant="standard">
|
||||
<FormTab label="Record Info">
|
||||
<div
|
||||
style={{
|
||||
columns: "3 auto",
|
||||
// display: "flex",
|
||||
width: "100%",
|
||||
// justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<TextInput disabled fullWidth source="id" />
|
||||
<TextInput disabled fullWidth source="created_at" />
|
||||
<TextInput disabled fullWidth source="updated_at" />
|
||||
</div>
|
||||
</FormTab>
|
||||
<FormTab label="Job Info">
|
||||
<div
|
||||
style={{
|
||||
columns: "3 auto",
|
||||
// display: "flex",
|
||||
width: "100%",
|
||||
// justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<ReferenceInput label="Shopid" source="shopid" reference="bodyshops">
|
||||
<SelectInput disabled optionText="shopname" />
|
||||
</ReferenceInput>
|
||||
<TextInput fullWidth source="ro_number" />
|
||||
<ReferenceInput label="Owner ID" source="ownerid" reference="owners">
|
||||
<AutocompleteInput
|
||||
matchSuggestion={(filter, choice) =>
|
||||
choice.ownr_fn &&
|
||||
choice.ownr_fn.toLowerCase().includes(filter.toLowerCase())
|
||||
}
|
||||
optionText={(record) =>
|
||||
`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
} (${record.ownr_ph1 || ""})`
|
||||
}
|
||||
/>
|
||||
</ReferenceInput>
|
||||
<ReferenceInput
|
||||
label="Vehicle Id"
|
||||
source="vehicleid"
|
||||
reference="vehicles"
|
||||
>
|
||||
<SelectInput optionText="v_vin" />
|
||||
</ReferenceInput>
|
||||
<ReferenceInput label="Shopid" source="shopid" reference="bodyshops">
|
||||
<SelectInput disabled optionText="shopname" />
|
||||
</ReferenceInput>
|
||||
<TextInput fullWidth source="ro_number" />
|
||||
<ReferenceInput label="Owner ID" source="ownerid" reference="owners">
|
||||
<AutocompleteInput
|
||||
matchSuggestion={(filter, choice) =>
|
||||
choice.ownr_fn &&
|
||||
choice.ownr_fn.toLowerCase().includes(filter.toLowerCase())
|
||||
}
|
||||
optionText={(record) =>
|
||||
`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
} (${record.ownr_ph1 || ""})`
|
||||
}
|
||||
/>
|
||||
</ReferenceInput>
|
||||
<ReferenceInput
|
||||
label="Vehicle Id"
|
||||
source="vehicleid"
|
||||
reference="vehicles"
|
||||
>
|
||||
<SelectInput optionText="v_vin" />
|
||||
</ReferenceInput>
|
||||
<TextInput fullWidth source="inproduction" />
|
||||
<TextInput fullWidth source="converted" />
|
||||
</div>
|
||||
</FormTab>
|
||||
|
||||
<TextInput fullWidth source="shopid" disabled />
|
||||
<TextInput fullWidth source="ro_number" />
|
||||
<TextInput fullWidth source="ownerid" />
|
||||
<TextInput fullWidth source="vehicleid" />
|
||||
<TextInput fullWidth source="labor_rate_id" />
|
||||
<TextInput fullWidth source="labor_rate_desc" />
|
||||
<TextInput fullWidth source="rate_lab" />
|
||||
<TextInput fullWidth source="rate_lad" />
|
||||
<TextInput fullWidth source="rate_lae" />
|
||||
<TextInput fullWidth source="rate_lar" />
|
||||
<TextInput fullWidth source="rate_las" />
|
||||
<TextInput fullWidth source="rate_laf" />
|
||||
<TextInput fullWidth source="rate_lam" />
|
||||
<TextInput fullWidth source="rate_lag" />
|
||||
<TextInput fullWidth source="rate_atp" />
|
||||
<TextInput fullWidth source="rate_lau" />
|
||||
<TextInput fullWidth source="rate_la1" />
|
||||
<TextInput fullWidth source="rate_la2" />
|
||||
<TextInput fullWidth source="rate_la3" />
|
||||
<TextInput fullWidth source="rate_la4" />
|
||||
<TextInput fullWidth source="rate_mapa" />
|
||||
<TextInput fullWidth source="rate_mash" />
|
||||
<TextInput fullWidth source="rate_mahw" />
|
||||
<TextInput fullWidth source="rate_ma2s" />
|
||||
<TextInput fullWidth source="rate_ma3s" />
|
||||
<TextInput fullWidth source="rate_ma2t" />
|
||||
<TextInput fullWidth source="rate_mabl" />
|
||||
<TextInput fullWidth source="rate_macs" />
|
||||
<TextInput fullWidth source="rate_matd" />
|
||||
<TextInput fullWidth source="federal_tax_rate" />
|
||||
<TextInput fullWidth source="state_tax_rate" />
|
||||
<TextInput fullWidth source="local_tax_rate" />
|
||||
<FormTab label="Labor Rates">
|
||||
<div
|
||||
style={{
|
||||
columns: "3 auto",
|
||||
// display: "flex",
|
||||
width: "100%",
|
||||
// justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<NumberInput fullWidth source="labor_rate_id" />
|
||||
<NumberInput fullWidth source="labor_rate_desc" />
|
||||
<NumberInput fullWidth source="rate_lab" />
|
||||
<NumberInput fullWidth source="rate_lad" />
|
||||
<NumberInput fullWidth source="rate_lae" />
|
||||
<NumberInput fullWidth source="rate_lar" />
|
||||
<NumberInput fullWidth source="rate_las" />
|
||||
<NumberInput fullWidth source="rate_laf" />
|
||||
<NumberInput fullWidth source="rate_lam" />
|
||||
<NumberInput fullWidth source="rate_lag" />
|
||||
<NumberInput fullWidth source="rate_atp" />
|
||||
<NumberInput fullWidth source="rate_lau" />
|
||||
<NumberInput fullWidth source="rate_la1" />
|
||||
<NumberInput fullWidth source="rate_la2" />
|
||||
<NumberInput fullWidth source="rate_la3" />
|
||||
<NumberInput fullWidth source="rate_la4" />
|
||||
<NumberInput fullWidth source="rate_mapa" />
|
||||
<NumberInput fullWidth source="rate_mash" />
|
||||
<NumberInput fullWidth source="rate_mahw" />
|
||||
<NumberInput fullWidth source="rate_ma2s" />
|
||||
<NumberInput fullWidth source="rate_ma3s" />
|
||||
<NumberInput fullWidth source="rate_ma2t" />
|
||||
<NumberInput fullWidth source="rate_mabl" />
|
||||
<NumberInput fullWidth source="rate_macs" />
|
||||
<NumberInput fullWidth source="rate_matd" />
|
||||
<NumberInput fullWidth source="federal_tax_rate" />
|
||||
<NumberInput fullWidth source="state_tax_rate" />
|
||||
<NumberInput fullWidth source="local_tax_rate" />
|
||||
</div>
|
||||
</FormTab>
|
||||
<FormTab label="Dates">
|
||||
<TextInput fullWidth source="scheduled_in" />
|
||||
<TextInput fullWidth source="actual_in" />
|
||||
<TextInput fullWidth source="scheduled_completion" />
|
||||
<TextInput fullWidth source="actual_completion" />
|
||||
<TextInput fullWidth source="scheduled_delivery" />
|
||||
<TextInput fullWidth source="actual_delivery" />
|
||||
<TextInput fullWidth source="invoice_date" />
|
||||
<TextInput fullWidth source="date_estimated" />
|
||||
<TextInput fullWidth source="date_open" />
|
||||
<TextInput fullWidth source="date_scheduled" />
|
||||
<TextInput fullWidth source="date_invoiced" />
|
||||
<TextInput fullWidth source="date_closed" />
|
||||
<TextInput fullWidth source="date_exported" />
|
||||
</FormTab>
|
||||
<FormTab label="Insurance info">
|
||||
<TextInput fullWidth source="est_co_nm" />
|
||||
<TextInput fullWidth source="est_addr1" />
|
||||
<TextInput fullWidth source="est_addr2" />
|
||||
@@ -59,15 +156,7 @@ const JobsEdit = (props) => (
|
||||
<TextInput fullWidth source="est_ea" />
|
||||
<TextInput fullWidth source="est_ct_ln" />
|
||||
<TextInput fullWidth source="est_ct_fn" />
|
||||
<TextInput fullWidth source="scheduled_in" />
|
||||
<TextInput fullWidth source="actual_in" />
|
||||
<TextInput fullWidth source="scheduled_completion" />
|
||||
<TextInput fullWidth source="actual_completion" />
|
||||
<TextInput fullWidth source="scheduled_delivery" />
|
||||
<TextInput fullWidth source="actual_delivery" />
|
||||
<TextInput fullWidth source="regie_number" />
|
||||
<TextInput fullWidth source="invoice_date" />
|
||||
<TextInput fullWidth source="inproduction" />
|
||||
<TextInput fullWidth source="statusid" />
|
||||
<TextInput fullWidth source="ins_co_id" />
|
||||
<TextInput fullWidth source="ins_co_nm" />
|
||||
@@ -98,15 +187,31 @@ const JobsEdit = (props) => (
|
||||
<TextInput fullWidth source="asgn_type" />
|
||||
<TextInput fullWidth source="clm_no" />
|
||||
<TextInput fullWidth source="clm_ofc_id" />
|
||||
<TextInput fullWidth source="date_estimated" />
|
||||
<TextInput fullWidth source="date_open" />
|
||||
<TextInput fullWidth source="date_scheduled" />
|
||||
<TextInput fullWidth source="date_invoiced" />
|
||||
<TextInput fullWidth source="date_closed" />
|
||||
<TextInput fullWidth source="date_exported" />
|
||||
<TextInput fullWidth source="clm_total" />
|
||||
<TextInput fullWidth source="owner_owing" />
|
||||
<TextInput fullWidth source="converted" />
|
||||
<TextInput fullWidth source="agt_co_id" />
|
||||
<TextInput fullWidth source="agt_co_nm" />
|
||||
<TextInput fullWidth source="agt_addr1" />
|
||||
<TextInput fullWidth source="agt_addr2" />
|
||||
<TextInput fullWidth source="agt_city" />
|
||||
<TextInput fullWidth source="agt_st" />
|
||||
<TextInput fullWidth source="agt_zip" />
|
||||
<TextInput fullWidth source="agt_ctry" />
|
||||
<TextInput fullWidth source="agt_ph1" />
|
||||
<TextInput fullWidth source="agt_ph1x" />
|
||||
<TextInput fullWidth source="agt_ph2" />
|
||||
<TextInput fullWidth source="agt_ph2x" />
|
||||
<TextInput fullWidth source="agt_fax" />
|
||||
<TextInput fullWidth source="agt_faxx" />
|
||||
<TextInput fullWidth source="agt_ct_ln" />
|
||||
<TextInput fullWidth source="agt_ct_fn" />
|
||||
<TextInput fullWidth source="agt_ct_ph" />
|
||||
<TextInput fullWidth source="agt_ct_phx" />
|
||||
<TextInput fullWidth source="agt_ea" />
|
||||
<TextInput fullWidth source="agt_lic_no" />
|
||||
<TextInput fullWidth source="loss_type" />
|
||||
<TextInput fullWidth source="loss_desc" />
|
||||
<TextInput fullWidth source="theft_ind" />
|
||||
<TextInput fullWidth source="cat_no" />
|
||||
<TextInput fullWidth source="tlos_ind" />
|
||||
<TextInput fullWidth source="ciecaid" />
|
||||
<TextInput fullWidth source="loss_date" />
|
||||
<TextInput fullWidth source="clm_ofc_nm" />
|
||||
@@ -133,31 +238,8 @@ const JobsEdit = (props) => (
|
||||
<TextInput fullWidth source="pay_date" />
|
||||
<TextInput fullWidth source="pay_chknm" />
|
||||
<TextInput fullWidth source="pay_amt" />
|
||||
<TextInput fullWidth source="agt_co_id" />
|
||||
<TextInput fullWidth source="agt_co_nm" />
|
||||
<TextInput fullWidth source="agt_addr1" />
|
||||
<TextInput fullWidth source="agt_addr2" />
|
||||
<TextInput fullWidth source="agt_city" />
|
||||
<TextInput fullWidth source="agt_st" />
|
||||
<TextInput fullWidth source="agt_zip" />
|
||||
<TextInput fullWidth source="agt_ctry" />
|
||||
<TextInput fullWidth source="agt_ph1" />
|
||||
<TextInput fullWidth source="agt_ph1x" />
|
||||
<TextInput fullWidth source="agt_ph2" />
|
||||
<TextInput fullWidth source="agt_ph2x" />
|
||||
<TextInput fullWidth source="agt_fax" />
|
||||
<TextInput fullWidth source="agt_faxx" />
|
||||
<TextInput fullWidth source="agt_ct_ln" />
|
||||
<TextInput fullWidth source="agt_ct_fn" />
|
||||
<TextInput fullWidth source="agt_ct_ph" />
|
||||
<TextInput fullWidth source="agt_ct_phx" />
|
||||
<TextInput fullWidth source="agt_ea" />
|
||||
<TextInput fullWidth source="agt_lic_no" />
|
||||
<TextInput fullWidth source="loss_type" />
|
||||
<TextInput fullWidth source="loss_desc" />
|
||||
<TextInput fullWidth source="theft_ind" />
|
||||
<TextInput fullWidth source="cat_no" />
|
||||
<TextInput fullWidth source="tlos_ind" />
|
||||
</FormTab>
|
||||
<FormTab label="Owner Data on Job">
|
||||
<TextInput fullWidth source="cust_pr" />
|
||||
<TextInput fullWidth source="insd_ln" />
|
||||
<TextInput fullWidth source="insd_fn" />
|
||||
@@ -193,6 +275,12 @@ const JobsEdit = (props) => (
|
||||
<TextInput fullWidth source="ownr_fax" />
|
||||
<TextInput fullWidth source="ownr_faxx" />
|
||||
<TextInput fullWidth source="ownr_ea" />
|
||||
</FormTab>
|
||||
<FormTab label="Financial">
|
||||
<TextInput fullWidth source="clm_total" />
|
||||
<TextInput fullWidth source="owner_owing" />
|
||||
</FormTab>
|
||||
<FormTab label="Other">
|
||||
<TextInput fullWidth source="area_of_damage" />
|
||||
<TextInput fullWidth source="loss_cat" />
|
||||
<TextInput fullWidth source="est_number" />
|
||||
@@ -247,8 +335,8 @@ const JobsEdit = (props) => (
|
||||
<TextInput fullWidth source="employee_body" />
|
||||
<TextInput fullWidth source="employee_refinish" />
|
||||
<TextInput fullWidth source="employee_prep" />
|
||||
</div>
|
||||
</SimpleForm>
|
||||
</FormTab>
|
||||
</TabbedForm>
|
||||
</Edit>
|
||||
);
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@ import React from "react";
|
||||
import {
|
||||
Datagrid,
|
||||
EditButton,
|
||||
|
||||
NumberField,
|
||||
ReferenceManyField,
|
||||
Show,
|
||||
|
||||
|
||||
Tab, TabbedShowLayout,
|
||||
TextField
|
||||
Tab,
|
||||
TabbedShowLayout,
|
||||
TextField,
|
||||
} from "react-admin";
|
||||
|
||||
const JobsShow = (props) => (
|
||||
@@ -31,7 +29,7 @@ const JobsShow = (props) => (
|
||||
label="Job Lines"
|
||||
>
|
||||
<Datagrid>
|
||||
<TextField source="id" />
|
||||
<TextField source="id" />
|
||||
|
||||
<TextField source="line_ref" />
|
||||
<TextField source="line_desc" />
|
||||
|
||||
467
admin/yarn.lock
467
admin/yarn.lock
@@ -2,16 +2,16 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@apollo/client@^3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.0.2.tgz#fadb2b39a0e32950baaef2566442cb3f6de74a52"
|
||||
integrity sha512-4ighan5Anlj4tK/tdUHs4Mi1njqXZ7AxRCVolz/H702DjPphAJfm+FRkIadPTmwz+OLO+d+tX+6V1VBshf02rg==
|
||||
"@apollo/client@^3.1.2":
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.1.2.tgz#e384f691706c46aef4d9234b63a03ccc06e9c33c"
|
||||
integrity sha512-GaA/J0CDSSNe0HVm1abeOIJA3M4fs9Ih7wF2z1AI2SLqv5TBLvwBxh0+0+jCSntPZ3gnDQvR7MHjmXota5V1LQ==
|
||||
dependencies:
|
||||
"@types/zen-observable" "^0.8.0"
|
||||
"@wry/context" "^0.5.2"
|
||||
"@wry/equality" "^0.1.9"
|
||||
"@wry/equality" "^0.2.0"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
graphql-tag "^2.10.4"
|
||||
graphql-tag "^2.11.0"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
optimism "^0.12.1"
|
||||
prop-types "^15.7.2"
|
||||
@@ -1022,13 +1022,20 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3":
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3":
|
||||
version "7.10.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c"
|
||||
integrity sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.9.2":
|
||||
version "7.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
|
||||
integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.10.4":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
|
||||
@@ -1123,16 +1130,16 @@
|
||||
resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.3.1.tgz#3c5f5d71129c88295e17e914e34b391ffda1723c"
|
||||
integrity sha512-63vVJ5NIBh/JF8l9LuPrQYSzFimk7zYHySQB4Dk9rVdJ8kV/vGQoVTvRu1UW05sEc2Ug5PqtEChtTHU+9hvPcA==
|
||||
|
||||
"@firebase/analytics@0.4.0":
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.4.0.tgz#15dfee56619af18c262d4b7cb2f1d4e7c25194de"
|
||||
integrity sha512-8DC2OBXGYxeeRxCh6eFnrrswNcKm2WsD8EeqGcl0F1P7J0bJ4Q+WpP3DvxofQZ/PtVHdAhzmfmt9r6Xa9mHnrQ==
|
||||
"@firebase/analytics@0.4.1":
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.4.1.tgz#0f1e6f4e56af11c3956b1652520095a1fbd2c418"
|
||||
integrity sha512-y5ZuhqX/PwLi0t7AKxNAi3NnlEwXe0rpknulUWUg3/1dALqtd2RrAOATQoV5FNnKK6YUH5UmK0Jb9KcSjsFeNw==
|
||||
dependencies:
|
||||
"@firebase/analytics-types" "0.3.1"
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/installations" "0.4.14"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/installations" "0.4.15"
|
||||
"@firebase/logger" "0.2.6"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
tslib "^1.11.1"
|
||||
|
||||
"@firebase/app-types@0.6.1":
|
||||
@@ -1140,15 +1147,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.6.1.tgz#dcbd23030a71c0c74fc95d4a3f75ba81653850e9"
|
||||
integrity sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==
|
||||
|
||||
"@firebase/app@0.6.8":
|
||||
version "0.6.8"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.6.8.tgz#e7ccf31cc1d01f16744d6d27c5f9ba8b64338f12"
|
||||
integrity sha512-Tm7Pi6Dtpx4FFKcpm0jcrZ/qI9oREBxmP3pWlw1jgDW4syRJHmN9/5DYvfFk6FAhj3FrY8E/6F+ngWJfqONotQ==
|
||||
"@firebase/app@0.6.9":
|
||||
version "0.6.9"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.6.9.tgz#e60412d9b6012afb73caef2a1353e1b4c4182954"
|
||||
integrity sha512-X2riRgK49IK8LCQ3j7BKLu3zqHDTJSaT6YgcLewtHuOVwtpHfGODiS1cL5VMvKm3ogxP84GA70tN3sdoL/vTog==
|
||||
dependencies:
|
||||
"@firebase/app-types" "0.6.1"
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/logger" "0.2.6"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
dom-storage "2.1.0"
|
||||
tslib "^1.11.1"
|
||||
xmlhttprequest "1.8.0"
|
||||
@@ -1170,12 +1177,12 @@
|
||||
dependencies:
|
||||
"@firebase/auth-types" "0.10.1"
|
||||
|
||||
"@firebase/component@0.1.16":
|
||||
version "0.1.16"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.1.16.tgz#7a0dbdfff1485d45b8485db87a982f053e68761a"
|
||||
integrity sha512-FvffvFN0LWgv1H/FIyruTECOL69Dhy+JfwoTq+mV39V8Mz9lNpo41etonL5AOr7KmXxYJVbNwkx0L9Ei88i7JA==
|
||||
"@firebase/component@0.1.17":
|
||||
version "0.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.1.17.tgz#2ce3e1aa060eccf0f06d20368ef9a32cf07c07be"
|
||||
integrity sha512-/tN5iLcFp9rdpTfCJPfQ/o2ziGHlDxOzNx6XD2FoHlu4pG/PPGu+59iRfQXIowBGhxcTGD/l7oJhZEY/PVg0KQ==
|
||||
dependencies:
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
tslib "^1.11.1"
|
||||
|
||||
"@firebase/database-types@0.5.1":
|
||||
@@ -1185,16 +1192,16 @@
|
||||
dependencies:
|
||||
"@firebase/app-types" "0.6.1"
|
||||
|
||||
"@firebase/database@0.6.8":
|
||||
version "0.6.8"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.6.8.tgz#28c9fb4e6a3322cdf9b5f58e768f21d9ac948840"
|
||||
integrity sha512-Psibz/LD9WBvZRS7A/kkYd5i5l6tBw49adSFmCM2ZJlKE9fxZhxay02AerwfXHiq3gPKVeqXUjBIRuHOWdEXmw==
|
||||
"@firebase/database@0.6.9":
|
||||
version "0.6.9"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.6.9.tgz#18a4bdc93b0b10c19a8ad4ff616bba196e5a16e0"
|
||||
integrity sha512-+X2dNFDpcLEcDRdXp2Hgkf0RnNz3AOIC+Y7UFMQYadm9buB+snXomlnlkMzOj6o+Cp3V7GnpBrKKeeFqzF6wGQ==
|
||||
dependencies:
|
||||
"@firebase/auth-interop-types" "0.1.5"
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/database-types" "0.5.1"
|
||||
"@firebase/logger" "0.2.6"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
faye-websocket "0.11.3"
|
||||
tslib "^1.11.1"
|
||||
|
||||
@@ -1203,15 +1210,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-1.12.0.tgz#511e572e946b07f5a603c90e078f0cd714923fac"
|
||||
integrity sha512-OqNxVb63wPZdUc7YnpacAW1WNIMSKERSewCRi+unCQ0YI0KNfrDSypyGCyel+S3GdOtKMk9KnvDknaGbnaFX4g==
|
||||
|
||||
"@firebase/firestore@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-1.16.1.tgz#ddf4e357b4d847abe6a57a89d3e2d5b950339176"
|
||||
integrity sha512-TGtvNIGHMEFFEuOSsRswou576GPZY39vXIsenn0B1Dqz9ACpyDtvAT9YdbG38srlPq7ZKwsP5x04LB43zZ6eAg==
|
||||
"@firebase/firestore@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-1.16.2.tgz#66eedeefab569331efc1ad9ab49a8f1c867a9163"
|
||||
integrity sha512-iIkAL860oD/QA1uYI9JBbWqBYFWd+DnuSj//BIbOGn3DNAruDFy07g8re1vn+0MMas9bMk6CZATJNCFPeH8AsQ==
|
||||
dependencies:
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/firestore-types" "1.12.0"
|
||||
"@firebase/logger" "0.2.6"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
"@firebase/webchannel-wrapper" "0.2.41"
|
||||
"@grpc/grpc-js" "^1.0.0"
|
||||
"@grpc/proto-loader" "^0.5.0"
|
||||
@@ -1222,12 +1229,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.3.17.tgz#348bf5528b238eeeeeae1d52e8ca547b21d33a94"
|
||||
integrity sha512-DGR4i3VI55KnYk4IxrIw7+VG7Q3gA65azHnZxo98Il8IvYLr2UTBlSh72dTLlDf25NW51HqvJgYJDKvSaAeyHQ==
|
||||
|
||||
"@firebase/functions@0.4.48":
|
||||
version "0.4.48"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.4.48.tgz#aee8efeacbfdd74834db0c1b44297f59c5bdddaf"
|
||||
integrity sha512-BwI/JzO/f/nquKG1IS3VqmwMaKEhvM58/08vTnp46krHBsOYqsdD9T2amz+HXGT9fe2HhDsUhgFE8D00S0vqbg==
|
||||
"@firebase/functions@0.4.49":
|
||||
version "0.4.49"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.4.49.tgz#cca60a2f8e188e020c7e5a5ecf075474885ffb03"
|
||||
integrity sha512-ma3+z1wMKervmEJCLWxwIjbSV+n3/BTfFPSZdTjt18Wgiso5q4BzEObFkorxaXZiyT3KpZ0qOO97lgcoth2hIA==
|
||||
dependencies:
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/functions-types" "0.3.17"
|
||||
"@firebase/messaging-types" "0.4.5"
|
||||
isomorphic-fetch "2.2.1"
|
||||
@@ -1238,14 +1245,14 @@
|
||||
resolved "https://registry.yarnpkg.com/@firebase/installations-types/-/installations-types-0.3.4.tgz#589a941d713f4f64bf9f4feb7f463505bab1afa2"
|
||||
integrity sha512-RfePJFovmdIXb6rYwtngyxuEcWnOrzdZd9m7xAW0gRxDIjBT20n3BOhjpmgRWXo/DAxRmS7bRjWAyTHY9cqN7Q==
|
||||
|
||||
"@firebase/installations@0.4.14":
|
||||
version "0.4.14"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.4.14.tgz#e0c240869bed834d1b5cc696bec0020e8fcb5f7b"
|
||||
integrity sha512-hQPsaU7wdTq3CFMtFQwZy6LgdXZAkXoUToV4O+ekPbjM65QzaGVogJVU8O2H6ADXoq37SarcUXKe86pcUWdFLA==
|
||||
"@firebase/installations@0.4.15":
|
||||
version "0.4.15"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.4.15.tgz#ec5a098aea6b5e3e29e73270eeaaf9791587d20a"
|
||||
integrity sha512-6uGgDocDGu5gI7FeDBDcLaH4npz0cm2f0kctOFK+5N1CyK8Tv2YGv5/uGqlrTtSwDW+8tgKNo/5XXJJOPr9Jsw==
|
||||
dependencies:
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/installations-types" "0.3.4"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
idb "3.0.2"
|
||||
tslib "^1.11.1"
|
||||
|
||||
@@ -1259,15 +1266,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@firebase/messaging-types/-/messaging-types-0.4.5.tgz#452572d3c5b7fa83659fdb1884450477229f5dc4"
|
||||
integrity sha512-sux4fgqr/0KyIxqzHlatI04Ajs5rc3WM+WmtCpxrKP1E5Bke8xu/0M+2oy4lK/sQ7nov9z15n3iltAHCgTRU3Q==
|
||||
|
||||
"@firebase/messaging@0.6.20":
|
||||
version "0.6.20"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.6.20.tgz#c6139dad753185706196972629e9235cdda8c2d6"
|
||||
integrity sha512-1MqyljXnbFBeHYhL6QInVM9aO5MW820yhNmOIVxk58wNXq4tOQLzqnKuvlgZ+ttgqlDzrIYiVf3EOHh5DptttQ==
|
||||
"@firebase/messaging@0.6.21":
|
||||
version "0.6.21"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.6.21.tgz#d301de72ad055c3f302b917b8a11373cd78c7431"
|
||||
integrity sha512-cunbFNCtUy25Zp4/jn5lenYUPqgHpjKNUwRjKc7vIzYb4IT2Vu/7kaEptO3K0KQBC6O0QV3ZtqQxKrI9aLiSHg==
|
||||
dependencies:
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/installations" "0.4.14"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/installations" "0.4.15"
|
||||
"@firebase/messaging-types" "0.4.5"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
idb "3.0.2"
|
||||
tslib "^1.11.1"
|
||||
|
||||
@@ -1276,16 +1283,16 @@
|
||||
resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.0.13.tgz#58ce5453f57e34b18186f74ef11550dfc558ede6"
|
||||
integrity sha512-6fZfIGjQpwo9S5OzMpPyqgYAUZcFzZxHFqOyNtorDIgNXq33nlldTL/vtaUZA8iT9TT5cJlCrF/jthKU7X21EA==
|
||||
|
||||
"@firebase/performance@0.3.9":
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.3.9.tgz#01e27616aca8486c7602e4f51c325c2e7caae6e8"
|
||||
integrity sha512-Fj22DZXRhhKv1OSUzDxX7AqpJUcDld6tzXK1yxOC8e3v1DFPQMQdM9FoG1m1b/Vrqa6pCCqnqG6gh6VPnEcAzQ==
|
||||
"@firebase/performance@0.3.10":
|
||||
version "0.3.10"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.3.10.tgz#b68336e23f4b5422bd67f6ce35e28293a6b8945e"
|
||||
integrity sha512-j/hsx2xfOO1hZulmz7KxemoTIVXxrv94rt79x8qO1HzysT7ziViNvQ9cQGjDZWwVSO29TpLH31GOWLVnwmnxWQ==
|
||||
dependencies:
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/installations" "0.4.14"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/installations" "0.4.15"
|
||||
"@firebase/logger" "0.2.6"
|
||||
"@firebase/performance-types" "0.0.13"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
tslib "^1.11.1"
|
||||
|
||||
"@firebase/polyfill@0.3.36":
|
||||
@@ -1302,16 +1309,16 @@
|
||||
resolved "https://registry.yarnpkg.com/@firebase/remote-config-types/-/remote-config-types-0.1.9.tgz#fe6bbe4d08f3b6e92fce30e4b7a9f4d6a96d6965"
|
||||
integrity sha512-G96qnF3RYGbZsTRut7NBX0sxyczxt1uyCgXQuH/eAfUCngxjEGcZQnBdy6mvSdqdJh5mC31rWPO4v9/s7HwtzA==
|
||||
|
||||
"@firebase/remote-config@0.1.25":
|
||||
version "0.1.25"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.1.25.tgz#93c5bda311d6c1302697d6148bdb33bb8dcb9e15"
|
||||
integrity sha512-8YWefBhy77HMbWXWdbenalx+IDY/XkS+iURQ9qRYvSIFYx6RL04DzlakZNOY9CQAcxTA+cTSt4NNlhjopBjf2Q==
|
||||
"@firebase/remote-config@0.1.26":
|
||||
version "0.1.26"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.1.26.tgz#62f448237bc46b986c27ac623b5cc5852007ea05"
|
||||
integrity sha512-B6+nARVNcswysd6C16nK5tdGECgEpr1wdH6LyqylEQ8hUxYWN18qe49b9uPu+ktaHq0gFLg03gayZvQs7fxJOg==
|
||||
dependencies:
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/installations" "0.4.14"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/installations" "0.4.15"
|
||||
"@firebase/logger" "0.2.6"
|
||||
"@firebase/remote-config-types" "0.1.9"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
tslib "^1.11.1"
|
||||
|
||||
"@firebase/storage-types@0.3.13":
|
||||
@@ -1319,20 +1326,20 @@
|
||||
resolved "https://registry.yarnpkg.com/@firebase/storage-types/-/storage-types-0.3.13.tgz#cd43e939a2ab5742e109eb639a313673a48b5458"
|
||||
integrity sha512-pL7b8d5kMNCCL0w9hF7pr16POyKkb3imOW7w0qYrhBnbyJTdVxMWZhb0HxCFyQWC0w3EiIFFmxoz8NTFZDEFog==
|
||||
|
||||
"@firebase/storage@0.3.40":
|
||||
version "0.3.40"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.3.40.tgz#4e6ea66d9c3ce489cd1a892940c57c3078853410"
|
||||
integrity sha512-xTUvSSXh8tNSlch4V+kNbw736H0z/lbW3rHlx1kZVnT8V5M4bXE+TEcG4WpqvcWH3p+N6N1bUorkDbOFgBrztw==
|
||||
"@firebase/storage@0.3.41":
|
||||
version "0.3.41"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.3.41.tgz#cba8946f980d70e68d52cfb110ad109592a645d0"
|
||||
integrity sha512-2imzI78HcB7FjUqXMRHsGLlZnTYkaCHBjJflSbypwLrEty0hreR6vx3ThOO5y0MFH93WwifqUFJAa+Twkx6CIA==
|
||||
dependencies:
|
||||
"@firebase/component" "0.1.16"
|
||||
"@firebase/component" "0.1.17"
|
||||
"@firebase/storage-types" "0.3.13"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/util" "0.3.0"
|
||||
tslib "^1.11.1"
|
||||
|
||||
"@firebase/util@0.2.50":
|
||||
version "0.2.50"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/util/-/util-0.2.50.tgz#77666b845dcb49bc217650aa296a7a8986c06b44"
|
||||
integrity sha512-vFE6+Jfc25u0ViSpFxxq0q5s+XmuJ/y7CL3ud79RQe+WLFFg+j0eH1t23k0yNSG9vZNM7h3uHRIXbV97sYLAyw==
|
||||
"@firebase/util@0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/util/-/util-0.3.0.tgz#c3e938192cde4e1c6260aecaaf22103add2352f5"
|
||||
integrity sha512-GTwC+FSLeCPc44/TXCDReNQ5FPRIS5cb8Gr1XcD1TgiNBOvmyx61Om2YLwHp2GnN++6m6xmwmXARm06HOukATA==
|
||||
dependencies:
|
||||
tslib "^1.11.1"
|
||||
|
||||
@@ -1835,17 +1842,6 @@
|
||||
"@svgr/plugin-svgo" "^4.3.1"
|
||||
loader-utils "^1.2.3"
|
||||
|
||||
"@testing-library/dom@*":
|
||||
version "7.21.4"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.21.4.tgz#24b045f3161b7c91fdb35da7c001908cdc99b55b"
|
||||
integrity sha512-IXjKRTAH31nQ+mx6q3IPw85RTLul8VlWBm1rxURoxDt7JI0HPlAAfbtrKTdeq83XYCYO7HSHogyV+OsD+6FX0Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.3"
|
||||
"@types/aria-query" "^4.2.0"
|
||||
aria-query "^4.2.2"
|
||||
dom-accessibility-api "^0.4.6"
|
||||
pretty-format "^25.5.0"
|
||||
|
||||
"@testing-library/dom@^5.6.1":
|
||||
version "5.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-5.6.1.tgz#705a1cb4a039b877c1e69e916824038e837ab637"
|
||||
@@ -1857,34 +1853,41 @@
|
||||
pretty-format "^24.8.0"
|
||||
wait-for-expect "^1.2.0"
|
||||
|
||||
"@testing-library/dom@^6.15.0":
|
||||
version "6.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-6.16.0.tgz#04ada27ed74ad4c0f0d984a1245bb29b1fd90ba9"
|
||||
integrity sha512-lBD88ssxqEfz0wFL6MeUyyWZfV/2cjEZZV3YRpb2IoJRej/4f1jB0TzqIOznTpfR1r34CNesrubxwIlAQ8zgPA==
|
||||
"@testing-library/dom@^7.17.1":
|
||||
version "7.21.8"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.21.8.tgz#b64b266264bff9135eba3b5c6d4ddc995a3371e6"
|
||||
integrity sha512-iK1rJubFoeD5gxCryokwh09tnJa1Y4doNDbNFYYqOqz6ELwB1+kEAwlezA5xwMi8QrK7xg+1/aBMzb9X/A/EmA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.8.4"
|
||||
"@sheerun/mutationobserver-shim" "^0.3.2"
|
||||
"@types/testing-library__dom" "^6.12.1"
|
||||
aria-query "^4.0.2"
|
||||
dom-accessibility-api "^0.3.0"
|
||||
pretty-format "^25.1.0"
|
||||
wait-for-expect "^3.0.2"
|
||||
"@babel/runtime" "^7.10.3"
|
||||
"@types/aria-query" "^4.2.0"
|
||||
aria-query "^4.2.2"
|
||||
dom-accessibility-api "^0.4.6"
|
||||
pretty-format "^25.5.0"
|
||||
|
||||
"@testing-library/jest-dom@^4.2.4":
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz#00dfa0cbdd837d9a3c2a7f3f0a248ea6e7b89742"
|
||||
integrity sha512-j31Bn0rQo12fhCWOUWy9fl7wtqkp7In/YP2p5ZFyRuiiB9Qs3g+hS4gAmDWONbAHcRmVooNJ5eOHQDCOmUFXHg==
|
||||
"@testing-library/jest-dom@^5.11.2":
|
||||
version "5.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.2.tgz#c49de331555c70127b5d7fc97344ad5265f4c54c"
|
||||
integrity sha512-s+rWJx+lanEGKqvOl4qJR0rGjCrxsEjj9qjxFlg4NV4/FRD7fnUUAWPHqwpyafNHfLYArs58FADgdn4UKmjFmw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.1"
|
||||
chalk "^2.4.1"
|
||||
css "^2.2.3"
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@types/testing-library__jest-dom" "^5.9.1"
|
||||
aria-query "^4.2.2"
|
||||
chalk "^3.0.0"
|
||||
css "^3.0.0"
|
||||
css.escape "^1.5.1"
|
||||
jest-diff "^24.0.0"
|
||||
jest-matcher-utils "^24.0.0"
|
||||
lodash "^4.17.11"
|
||||
pretty-format "^24.0.0"
|
||||
jest-diff "^25.1.0"
|
||||
jest-matcher-utils "^25.1.0"
|
||||
lodash "^4.17.15"
|
||||
redent "^3.0.0"
|
||||
|
||||
"@testing-library/react@^10.4.8":
|
||||
version "10.4.8"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.4.8.tgz#5eb730291b8fd81cdb2d8877770d060b044ae4a4"
|
||||
integrity sha512-clgpFR6QHiRRcdhFfAKDhH8UXpNASyfkkANhtCsCVBnai+O+mK1rGtMES+Apc7ql5Wyxu7j8dcLiC4pV5VblHA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.3"
|
||||
"@testing-library/dom" "^7.17.1"
|
||||
|
||||
"@testing-library/react@^8.0.7":
|
||||
version "8.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-8.0.9.tgz#1ecd96bc3471b06dd2f9763b6e53a7ace28a54a2"
|
||||
@@ -1893,19 +1896,12 @@
|
||||
"@babel/runtime" "^7.5.5"
|
||||
"@testing-library/dom" "^5.6.1"
|
||||
|
||||
"@testing-library/react@^9.3.2":
|
||||
version "9.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-9.5.0.tgz#71531655a7890b61e77a1b39452fbedf0472ca5e"
|
||||
integrity sha512-di1b+D0p+rfeboHO5W7gTVeZDIK5+maEgstrZbWZSSvxDyfDRkkyBE1AJR5Psd6doNldluXlCWqXriUfqu/9Qg==
|
||||
"@testing-library/user-event@^12.1.0":
|
||||
version "12.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-12.1.0.tgz#a2597419466a93e338c91baa7bb22d4da0309d1d"
|
||||
integrity sha512-aH/XuNFpPD6dA+fh754EGqKeAzpH66HpLJYkv9vOAih2yGmTM8JiZ8uisQDGWRPkc6sxE2zCqDwLR4ZskhRCxw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.8.4"
|
||||
"@testing-library/dom" "^6.15.0"
|
||||
"@types/testing-library__react" "^9.1.2"
|
||||
|
||||
"@testing-library/user-event@^7.1.2":
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-7.2.1.tgz#2ad4e844175a3738cb9e7064be5ea070b8863a1c"
|
||||
integrity sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA==
|
||||
"@babel/runtime" "^7.10.2"
|
||||
|
||||
"@types/aria-query@^4.2.0":
|
||||
version "4.2.0"
|
||||
@@ -1989,6 +1985,14 @@
|
||||
"@types/istanbul-lib-coverage" "*"
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/jest@*":
|
||||
version "26.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.8.tgz#f5c5559cf25911ce227f7ce30f1f160f24966369"
|
||||
integrity sha512-eo3VX9jGASSuv680D4VQ89UmuLZneNxv2MCZjfwlInav05zXVJTzfc//lavdV0GPwSxsXJTy2jALscB7Acqg0g==
|
||||
dependencies:
|
||||
jest-diff "^25.2.1"
|
||||
pretty-format "^25.2.1"
|
||||
|
||||
"@types/json-schema@^7.0.3":
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
|
||||
@@ -2024,7 +2028,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||
|
||||
"@types/prop-types@*":
|
||||
"@types/prop-types@*", "@types/prop-types@^15.7.3":
|
||||
version "15.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
||||
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
||||
@@ -2034,13 +2038,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
|
||||
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
|
||||
|
||||
"@types/react-dom@*":
|
||||
version "16.9.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423"
|
||||
integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-transition-group@^4.2.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d"
|
||||
@@ -2061,28 +2058,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
|
||||
|
||||
"@types/testing-library__dom@*":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/testing-library__dom/-/testing-library__dom-7.5.0.tgz#e0a00dd766983b1d6e9d10d33e708005ce6ad13e"
|
||||
integrity sha512-mj1aH4cj3XUpMEgVpognma5kHVtbm6U6cHZmEFzCRiXPvKkuHrFr3+yXdGLXvfFRBaQIVshPGHI+hGTOJlhS/g==
|
||||
"@types/testing-library__jest-dom@^5.9.1":
|
||||
version "5.9.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.2.tgz#59e4771a1cf87d51e89a5cc8195cd3b647cba322"
|
||||
integrity sha512-K7nUSpH/5i8i0NagTJ+uFUDRueDlnMNhJtMjMwTGPPSqyImbWC/hgKPDCKt6Phu2iMJg2kWqlax+Ucj2DKMwpA==
|
||||
dependencies:
|
||||
"@testing-library/dom" "*"
|
||||
|
||||
"@types/testing-library__dom@^6.12.1":
|
||||
version "6.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/testing-library__dom/-/testing-library__dom-6.14.0.tgz#1aede831cb4ed4a398448df5a2c54b54a365644e"
|
||||
integrity sha512-sMl7OSv0AvMOqn1UJ6j1unPMIHRXen0Ita1ujnMX912rrOcawe4f7wu0Zt9GIQhBhJvH2BaibqFgQ3lP+Pj2hA==
|
||||
dependencies:
|
||||
pretty-format "^24.3.0"
|
||||
|
||||
"@types/testing-library__react@^9.1.2":
|
||||
version "9.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-9.1.3.tgz#35eca61cc6ea923543796f16034882a1603d7302"
|
||||
integrity sha512-iCdNPKU3IsYwRK9JieSYAiX0+aYDXOGAmrC/3/M7AqqSDKnWWVv07X+Zk1uFSL7cMTUYzv4lQRfohucEocn5/w==
|
||||
dependencies:
|
||||
"@types/react-dom" "*"
|
||||
"@types/testing-library__dom" "*"
|
||||
pretty-format "^25.1.0"
|
||||
"@types/jest" "*"
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "15.0.0"
|
||||
@@ -2312,13 +2293,20 @@
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@wry/equality@^0.1.2", "@wry/equality@^0.1.9":
|
||||
"@wry/equality@^0.1.2":
|
||||
version "0.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790"
|
||||
integrity sha512-mwEVBDUVODlsQQ5dfuLUS5/Tf7jqUKyhKYHmVi4fPB6bDMOfWvUPJmKgS1Z7Za/sOI3vzWt4+O7yCiL/70MogA==
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@wry/equality@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.2.0.tgz#a312d1b6a682d0909904c2bcd355b02303104fb7"
|
||||
integrity sha512-Y4d+WH6hs+KZJUC8YKLYGarjGekBrhslDbf/R20oV+AakHPINSitHfDRQz3EGcEWc1luXYNUvMhawWtZVWNGvQ==
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@xtuc/ieee754@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
|
||||
@@ -2641,7 +2629,7 @@ aria-query@3.0.0, aria-query@^3.0.0:
|
||||
ast-types-flow "0.0.7"
|
||||
commander "^2.11.0"
|
||||
|
||||
aria-query@^4.0.2, aria-query@^4.2.2:
|
||||
aria-query@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
|
||||
integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==
|
||||
@@ -4047,7 +4035,7 @@ css.escape@^1.5.1:
|
||||
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
|
||||
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
|
||||
|
||||
css@^2.0.0, css@^2.2.3:
|
||||
css@^2.0.0:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
|
||||
integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==
|
||||
@@ -4057,6 +4045,15 @@ css@^2.0.0, css@^2.2.3:
|
||||
source-map-resolve "^0.5.2"
|
||||
urix "^0.1.0"
|
||||
|
||||
css@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
|
||||
integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
|
||||
dependencies:
|
||||
inherits "^2.0.4"
|
||||
source-map "^0.6.1"
|
||||
source-map-resolve "^0.6.0"
|
||||
|
||||
cssdb@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0"
|
||||
@@ -4352,6 +4349,11 @@ diff-sequences@^24.9.0:
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
|
||||
integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==
|
||||
|
||||
diff-sequences@^25.2.6:
|
||||
version "25.2.6"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd"
|
||||
integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==
|
||||
|
||||
diffie-hellman@^5.0.0:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
|
||||
@@ -4411,11 +4413,6 @@ doctrine@^3.0.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
dom-accessibility-api@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz#511e5993dd673b97c87ea47dba0e3892f7e0c983"
|
||||
integrity sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA==
|
||||
|
||||
dom-accessibility-api@^0.4.6:
|
||||
version "0.4.6"
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.4.6.tgz#f3f2af68aee01b1c862f37918d41841bb1aaf92a"
|
||||
@@ -5315,25 +5312,25 @@ find-up@^3.0.0:
|
||||
dependencies:
|
||||
locate-path "^3.0.0"
|
||||
|
||||
firebase@^7.17.0:
|
||||
version "7.17.0"
|
||||
resolved "https://registry.yarnpkg.com/firebase/-/firebase-7.17.0.tgz#85dcd7c85d6dbcba7740dd5920a7f56bf8177e12"
|
||||
integrity sha512-+y7c1pCj8xp98CIDhVjg0rKhGtsFskGB8hyhjsyp549Upwa0cropdK5emCFTmMIbvDjZmP8rTuuDXPBeREAaCg==
|
||||
firebase@^7.17.1:
|
||||
version "7.17.1"
|
||||
resolved "https://registry.yarnpkg.com/firebase/-/firebase-7.17.1.tgz#6b2566d91a820a7993e3d2c75435f8baaabb58bb"
|
||||
integrity sha512-g2Wkk2fz8VoeSrxv2PIQizm2j74EtbpxQ+wd2AvH2iEF5LRaJOsk3zVBtIlyJIQ3vGTmlutIxtyyoDAQcPO9TA==
|
||||
dependencies:
|
||||
"@firebase/analytics" "0.4.0"
|
||||
"@firebase/app" "0.6.8"
|
||||
"@firebase/analytics" "0.4.1"
|
||||
"@firebase/app" "0.6.9"
|
||||
"@firebase/app-types" "0.6.1"
|
||||
"@firebase/auth" "0.14.9"
|
||||
"@firebase/database" "0.6.8"
|
||||
"@firebase/firestore" "1.16.1"
|
||||
"@firebase/functions" "0.4.48"
|
||||
"@firebase/installations" "0.4.14"
|
||||
"@firebase/messaging" "0.6.20"
|
||||
"@firebase/performance" "0.3.9"
|
||||
"@firebase/database" "0.6.9"
|
||||
"@firebase/firestore" "1.16.2"
|
||||
"@firebase/functions" "0.4.49"
|
||||
"@firebase/installations" "0.4.15"
|
||||
"@firebase/messaging" "0.6.21"
|
||||
"@firebase/performance" "0.3.10"
|
||||
"@firebase/polyfill" "0.3.36"
|
||||
"@firebase/remote-config" "0.1.25"
|
||||
"@firebase/storage" "0.3.40"
|
||||
"@firebase/util" "0.2.50"
|
||||
"@firebase/remote-config" "0.1.26"
|
||||
"@firebase/storage" "0.3.41"
|
||||
"@firebase/util" "0.3.0"
|
||||
|
||||
flat-cache@^2.0.1:
|
||||
version "2.0.1"
|
||||
@@ -5651,11 +5648,16 @@ graphql-ast-types-browser@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/graphql-ast-types-browser/-/graphql-ast-types-browser-1.0.2.tgz#474305af7e76f9692df6e50a88fb668ce258c4a4"
|
||||
integrity sha512-QuKZ+Et3dE7SyO5c41eNPlJc7+HwQxOzHfmIhqzj4cUgAGyhSwVkKb7K24zom8y6y0VnG7Xb3RRypjIVvfIevQ==
|
||||
|
||||
graphql-tag@^2.10.1, graphql-tag@^2.10.4, graphql-tag@^2.4.2:
|
||||
graphql-tag@^2.10.1, graphql-tag@^2.4.2:
|
||||
version "2.10.4"
|
||||
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.4.tgz#2f301a98219be8b178a6453bb7e33b79b66d8f83"
|
||||
integrity sha512-O7vG5BT3w6Sotc26ybcvLKNTdfr4GfsIVMD+LdYqXCeJIYPRyp8BIsDOUtxw7S1PYvRw5vH3278J2EDezR6mfA==
|
||||
|
||||
graphql-tag@^2.11.0:
|
||||
version "2.11.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.11.0.tgz#1deb53a01c46a7eb401d6cb59dec86fa1cccbffd"
|
||||
integrity sha512-VmsD5pJqWJnQZMUeRwrDhfgoyqcfwEkvtpANqcoUG8/tOLkwNgU9mzub/Mc78OJMhHjx7gfAMTxzdG43VGg3bA==
|
||||
|
||||
graphql@^15.3.0:
|
||||
version "15.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278"
|
||||
@@ -6109,7 +6111,7 @@ inflight@^1.0.4:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
@@ -6663,7 +6665,7 @@ jest-config@^24.9.0:
|
||||
pretty-format "^24.9.0"
|
||||
realpath-native "^1.1.0"
|
||||
|
||||
jest-diff@^24.0.0, jest-diff@^24.9.0:
|
||||
jest-diff@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da"
|
||||
integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==
|
||||
@@ -6673,6 +6675,16 @@ jest-diff@^24.0.0, jest-diff@^24.9.0:
|
||||
jest-get-type "^24.9.0"
|
||||
pretty-format "^24.9.0"
|
||||
|
||||
jest-diff@^25.1.0, jest-diff@^25.2.1, jest-diff@^25.5.0:
|
||||
version "25.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9"
|
||||
integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
diff-sequences "^25.2.6"
|
||||
jest-get-type "^25.2.6"
|
||||
pretty-format "^25.5.0"
|
||||
|
||||
jest-docblock@^24.3.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2"
|
||||
@@ -6731,6 +6743,11 @@ jest-get-type@^24.9.0:
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e"
|
||||
integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==
|
||||
|
||||
jest-get-type@^25.2.6:
|
||||
version "25.2.6"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877"
|
||||
integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==
|
||||
|
||||
jest-haste-map@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d"
|
||||
@@ -6780,7 +6797,7 @@ jest-leak-detector@^24.9.0:
|
||||
jest-get-type "^24.9.0"
|
||||
pretty-format "^24.9.0"
|
||||
|
||||
jest-matcher-utils@^24.0.0, jest-matcher-utils@^24.9.0:
|
||||
jest-matcher-utils@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073"
|
||||
integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==
|
||||
@@ -6790,6 +6807,16 @@ jest-matcher-utils@^24.0.0, jest-matcher-utils@^24.9.0:
|
||||
jest-get-type "^24.9.0"
|
||||
pretty-format "^24.9.0"
|
||||
|
||||
jest-matcher-utils@^25.1.0:
|
||||
version "25.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz#fbc98a12d730e5d2453d7f1ed4a4d948e34b7867"
|
||||
integrity sha512-VWI269+9JS5cpndnpCwm7dy7JtGQT30UHfrnM3mXl22gHGt/b7NkjBqXfbhZ8V4B7ANUsjK18PlSBmG0YH7gjw==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
jest-diff "^25.5.0"
|
||||
jest-get-type "^25.2.6"
|
||||
pretty-format "^25.5.0"
|
||||
|
||||
jest-message-util@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3"
|
||||
@@ -9346,7 +9373,7 @@ pretty-error@^2.1.1:
|
||||
renderkid "^2.0.1"
|
||||
utila "~0.4"
|
||||
|
||||
pretty-format@^24.0.0, pretty-format@^24.3.0, pretty-format@^24.8.0, pretty-format@^24.9.0:
|
||||
pretty-format@^24.8.0, pretty-format@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9"
|
||||
integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==
|
||||
@@ -9356,7 +9383,7 @@ pretty-format@^24.0.0, pretty-format@^24.3.0, pretty-format@^24.8.0, pretty-form
|
||||
ansi-styles "^3.2.0"
|
||||
react-is "^16.8.4"
|
||||
|
||||
pretty-format@^25.1.0, pretty-format@^25.5.0:
|
||||
pretty-format@^25.2.1, pretty-format@^25.5.0:
|
||||
version "25.5.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a"
|
||||
integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==
|
||||
@@ -9563,10 +9590,10 @@ querystringify@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
|
||||
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
||||
|
||||
ra-core@^3.7.1:
|
||||
version "3.7.1"
|
||||
resolved "https://registry.yarnpkg.com/ra-core/-/ra-core-3.7.1.tgz#369453d8cce3a1e8ef7d01a6bb2dcd6bbd5faa1c"
|
||||
integrity sha512-T6gYppeTMoG4qbpD4cHJ76EQTuf0b6DedMSL8ekFuZdh+MDtpGywUlCzkcSQK9J4Ck31prtVvONXGm9uwEyEyg==
|
||||
ra-core@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/ra-core/-/ra-core-3.7.2.tgz#efbc873cfbf840c4c53e9a7317d6fd526bf55b39"
|
||||
integrity sha512-cm/RGWX9WUoVVTJkdKKhOOx6tG7IOaY9p+YxbFvweau0Gi9sK+i9t687ifrENukW2YCxIeKpyImjKAT7l8BiLA==
|
||||
dependencies:
|
||||
"@testing-library/react" "^8.0.7"
|
||||
classnames "~2.2.5"
|
||||
@@ -9601,25 +9628,25 @@ ra-data-hasura-graphql@^0.1.12:
|
||||
minimist ">=1.2.3"
|
||||
ra-data-graphql "^3.6.1"
|
||||
|
||||
ra-i18n-polyglot@^3.7.1:
|
||||
version "3.7.1"
|
||||
resolved "https://registry.yarnpkg.com/ra-i18n-polyglot/-/ra-i18n-polyglot-3.7.1.tgz#68ae7ec78c43f2700dd0921d7263546ae35c5b34"
|
||||
integrity sha512-BWYJKGp0nZP8ATsYqfWkvQpyaVx4KrYw/XT6Taf8HfhfPqZaiOQUhfw4a9co3Nm/0Gu3mFdHq5CVMDsEZL1r+w==
|
||||
ra-i18n-polyglot@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/ra-i18n-polyglot/-/ra-i18n-polyglot-3.7.2.tgz#66589baf838a24ab71f251b1c41ec75bcc294f1f"
|
||||
integrity sha512-WFb6tw/lBnmne6BZit3bO0R45jTRBIkbd/8UMNMRBpZn9vFdphF9NShrNuVPLGnxQPBoz0CBRSTEvyKX7HGnTg==
|
||||
dependencies:
|
||||
node-polyglot "^2.2.2"
|
||||
ra-core "^3.7.1"
|
||||
ra-core "^3.7.2"
|
||||
|
||||
ra-language-english@^3.7.1:
|
||||
version "3.7.1"
|
||||
resolved "https://registry.yarnpkg.com/ra-language-english/-/ra-language-english-3.7.1.tgz#ed1aa4dd827454572fcc43a53c3a2944d5619d68"
|
||||
integrity sha512-85jgvpEdRgfQ1SVVVudUh7AOlmhbiLhT/iLn5tXO9N7HCudK7Fno37y+txjrPywki6Vs/XGkRLG/eNsrYmyMFQ==
|
||||
ra-language-english@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/ra-language-english/-/ra-language-english-3.7.2.tgz#19a929cba73c03d34aa0586145005a4759cc2c81"
|
||||
integrity sha512-1TQPTDgJ4gvF6uh7FcJhWqRUmi7P4Mo1Oy5e57BkrFO6M50Fl+e3McrI0grDQlSbn2bcFRNm13tWK/Z+KXdGeQ==
|
||||
dependencies:
|
||||
ra-core "^3.7.1"
|
||||
ra-core "^3.7.2"
|
||||
|
||||
ra-ui-materialui@^3.7.1:
|
||||
version "3.7.1"
|
||||
resolved "https://registry.yarnpkg.com/ra-ui-materialui/-/ra-ui-materialui-3.7.1.tgz#111883d4d27c197f3171e58a836811839619f509"
|
||||
integrity sha512-EA5z/2fnqv1HTJuzXCoxZoCIgohHY+C+zVjDjEBQ/ABa/PosZT+epOsKK9Gn4gLuHbLHJCwdjcOZXJHH6F1jiQ==
|
||||
ra-ui-materialui@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/ra-ui-materialui/-/ra-ui-materialui-3.7.2.tgz#1afeb943afc1940787e91094f4fbca72cd082f4c"
|
||||
integrity sha512-dNmgkZ2YDmXMrCRLLhSBBjSzM9B1x5qsSU0C4az2LyDnniphE4UOjkUCSj0DpgzVC4sevZub5H7QnjpIF3LZcQ==
|
||||
dependencies:
|
||||
autosuggest-highlight "^3.1.1"
|
||||
classnames "~2.2.5"
|
||||
@@ -9632,7 +9659,7 @@ ra-ui-materialui@^3.7.1:
|
||||
prop-types "^15.7.0"
|
||||
query-string "^5.1.1"
|
||||
react-dropzone "^10.1.7"
|
||||
react-transition-group "^4.3.0"
|
||||
react-transition-group "^4.4.1"
|
||||
recompose "~0.26.0"
|
||||
|
||||
raf@^3.4.1:
|
||||
@@ -9672,10 +9699,10 @@ raw-body@2.4.0:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
react-admin@^3.7.1:
|
||||
version "3.7.1"
|
||||
resolved "https://registry.yarnpkg.com/react-admin/-/react-admin-3.7.1.tgz#315573908361678125085005589c51512be2235e"
|
||||
integrity sha512-eVOdvh7HIc83BmoFjZNTcRU5l9XNhk6rHn5vBu1zcl5i//91jPw0NTS1uxG18STjiERckh/DVphELC5tFRIGwA==
|
||||
react-admin@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/react-admin/-/react-admin-3.7.2.tgz#f1c2895c208c06f63bc8f5eb952ad232a225de18"
|
||||
integrity sha512-VK99I+s1Dd1f4lFCUY3BRdTO6P1c6Yg1r/px0ZbbM6WyJ7+qFRVoXEhDlIB494WEcjZoJKZSDB464fyxePEWtA==
|
||||
dependencies:
|
||||
"@material-ui/core" "^4.3.3"
|
||||
"@material-ui/icons" "^4.2.1"
|
||||
@@ -9683,10 +9710,10 @@ react-admin@^3.7.1:
|
||||
connected-react-router "^6.5.2"
|
||||
final-form "^4.18.5"
|
||||
final-form-arrays "^3.0.1"
|
||||
ra-core "^3.7.1"
|
||||
ra-i18n-polyglot "^3.7.1"
|
||||
ra-language-english "^3.7.1"
|
||||
ra-ui-materialui "^3.7.1"
|
||||
ra-core "^3.7.2"
|
||||
ra-i18n-polyglot "^3.7.2"
|
||||
ra-language-english "^3.7.2"
|
||||
ra-ui-materialui "^3.7.2"
|
||||
react-final-form "^6.3.3"
|
||||
react-final-form-arrays "^3.1.1"
|
||||
react-redux "^7.1.0"
|
||||
@@ -9775,6 +9802,13 @@ react-final-form@^6.3.3:
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.0"
|
||||
|
||||
react-icons@^3.10.0:
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-3.10.0.tgz#6c217a2dde2e8fa8d293210023914b123f317297"
|
||||
integrity sha512-WsQ5n1JToG9VixWilSo1bHv842Cj5aZqTGiS3Ud47myF6aK7S/IUY2+dHcBdmkQcCFRuHsJ9OMUI0kTDfjyZXQ==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
|
||||
react-is@^16.12.0, react-is@^16.5.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
@@ -9880,7 +9914,7 @@ react-scripts@3.4.1:
|
||||
optionalDependencies:
|
||||
fsevents "2.1.2"
|
||||
|
||||
react-transition-group@^4.3.0, react-transition-group@^4.4.0:
|
||||
react-transition-group@^4.4.0, react-transition-group@^4.4.1:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
|
||||
integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
|
||||
@@ -10727,6 +10761,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
|
||||
source-map-url "^0.4.0"
|
||||
urix "^0.1.0"
|
||||
|
||||
source-map-resolve@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
|
||||
integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
|
||||
dependencies:
|
||||
atob "^2.1.2"
|
||||
decode-uri-component "^0.2.0"
|
||||
|
||||
source-map-support@^0.5.6, source-map-support@~0.5.12:
|
||||
version "0.5.16"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
|
||||
@@ -11692,11 +11734,6 @@ wait-for-expect@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.3.0.tgz#65241ce355425f907f5d127bdb5e72c412ff830c"
|
||||
integrity sha512-8fJU7jiA96HfGPt+P/UilelSAZfhMBJ52YhKzlmZQvKEZU2EcD1GQ0yqGB6liLdHjYtYAoGVigYwdxr5rktvzA==
|
||||
|
||||
wait-for-expect@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-3.0.2.tgz#d2f14b2f7b778c9b82144109c8fa89ceaadaa463"
|
||||
integrity sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag==
|
||||
|
||||
walker@^1.0.7, walker@~1.0.5:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<babeledit_project be_version="2.7.1" version="1.2">
|
||||
<babeledit_project version="1.2" be_version="2.7.1">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -526,6 +526,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>completingjobs</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>history</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -547,6 +568,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>nocompletingjobs</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>nodateselected</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -1190,6 +1232,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>enforce_class</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>federal_tax_id</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -1337,6 +1400,69 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>md_categories</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>md_classes</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>md_ins_cos</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>md_referral_sources</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -1463,6 +1589,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>prodtargethrs</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>responsibilitycenter</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -3000,6 +3147,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>rbac</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>
|
||||
<folder_node>
|
||||
<name>responsibilitycenters</name>
|
||||
<children>
|
||||
@@ -6711,6 +6879,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>hours</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>in</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -7194,6 +7383,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>view</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>yes</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -7309,6 +7519,111 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>newversionmessage</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>newversiontitle</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>notfoundsub</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>notfoundtitle</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>rbacunauth</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>unsavedchanges</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -9985,6 +10300,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>recalculate</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>reconcile</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -10048,6 +10384,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>viewdetail</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>
|
||||
@@ -10499,6 +10856,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>category</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>ccc</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -10646,6 +11024,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>class</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>clm_no</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -13003,6 +13402,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>tax_registration_number</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>towing_payable</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -13176,27 +13596,6 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>dedinfo</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>estdates</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -13218,27 +13617,6 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>inscoinfo</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>laborrates</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -13281,6 +13659,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>other</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>repairdates</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -13711,6 +14110,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cost_labor</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>cost_parts</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>
|
||||
<folder_node>
|
||||
<name>create</name>
|
||||
<children>
|
||||
@@ -14204,6 +14645,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>labortotals</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>lines</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -16004,28 +16466,7 @@
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>financials</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>insurance</name>
|
||||
<name>general</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
@@ -16087,6 +16528,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>rates</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>repairdata</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -16108,6 +16570,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>totals</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>
|
||||
@@ -19025,6 +19508,32 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>successes</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>removed</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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
@@ -20385,6 +20894,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>accounting-payments</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>accounting-receivables</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -20451,6 +20981,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>accounting-payments</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>accounting-receivables</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -2,66 +2,69 @@
|
||||
"name": "bodyshop",
|
||||
"version": "0.1.0001",
|
||||
"private": true,
|
||||
"proxy": "https://localhost:5000",
|
||||
"proxy": "http://localhost:5000",
|
||||
"dependencies": {
|
||||
"@lourenci/react-kanban": "^2.0.0",
|
||||
"@stripe/react-stripe-js": "^1.1.2",
|
||||
"@stripe/stripe-js": "^1.8.0",
|
||||
"@tanem/react-nprogress": "^3.0.34",
|
||||
"@stripe/stripe-js": "^1.9.0",
|
||||
"@tanem/react-nprogress": "^3.0.40",
|
||||
"@tinymce/tinymce-react": "^3.6.0",
|
||||
"antd": "^4.4.2",
|
||||
"antd": "^4.6.1",
|
||||
"apollo-boost": "^0.4.9",
|
||||
"apollo-link-context": "^1.0.20",
|
||||
"apollo-link-error": "^1.1.13",
|
||||
"apollo-link-logger": "^1.2.3",
|
||||
"apollo-link-retry": "^2.2.16",
|
||||
"apollo-link-ws": "^1.0.20",
|
||||
"axios": "^0.19.2",
|
||||
"axios": "^0.20.0",
|
||||
"dinero.js": "^1.8.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"fingerprintjs2": "^2.1.0",
|
||||
"firebase": "^7.16.0",
|
||||
"fingerprintjs2": "^2.1.2",
|
||||
"firebase": "^7.19.0",
|
||||
"graphql": "^15.3.0",
|
||||
"i18next": "^19.6.0",
|
||||
"i18next-browser-languagedetector": "^5.0.0",
|
||||
"logrocket": "^1.0.9",
|
||||
"i18next": "^19.7.0",
|
||||
"i18next-browser-languagedetector": "^6.0.1",
|
||||
"inline-css": "^2.6.3",
|
||||
"logrocket": "^1.0.11",
|
||||
"moment-business-days": "^1.2.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"phone": "^2.4.13",
|
||||
"phone": "^2.4.15",
|
||||
"prop-types": "^15.7.2",
|
||||
"query-string": "^6.13.1",
|
||||
"react": "^16.13.1",
|
||||
"react-apollo": "^3.1.5",
|
||||
"react-barcode": "^1.4.0",
|
||||
"react-big-calendar": "^0.26.0",
|
||||
"react-big-calendar": "^0.26.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-drag-listview": "^0.1.7",
|
||||
"react-email-editor": "^1.1.1",
|
||||
"react-ga": "^3.1.2",
|
||||
"react-grid-gallery": "^0.5.5",
|
||||
"react-grid-layout": "^0.18.3",
|
||||
"react-i18next": "^11.7.0",
|
||||
"react-icons": "^3.10.0",
|
||||
"react-image-file-resizer": "^0.3.1",
|
||||
"react-grid-layout": "^1.0.0",
|
||||
"react-i18next": "^11.7.1",
|
||||
"react-icons": "^3.11.0",
|
||||
"react-image-file-resizer": "^0.3.6",
|
||||
"react-moment": "^0.9.7",
|
||||
"react-number-format": "^4.4.1",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-redux": "^7.2.1",
|
||||
"react-resizable": "^1.10.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "3.4.1",
|
||||
"react-trello": "^2.2.7",
|
||||
"react-virtualized": "^9.21.2",
|
||||
"react-scripts": "3.4.3",
|
||||
"react-trello": "^2.2.8",
|
||||
"react-virtualized": "^9.22.2",
|
||||
"recharts": "^1.8.5",
|
||||
"redux": "^4.0.5",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-saga": "^1.1.3",
|
||||
"redux-state-sync": "^3.1.1",
|
||||
"redux-state-sync": "^3.1.2",
|
||||
"reselect": "^4.0.0",
|
||||
"styled-components": "^5.1.1",
|
||||
"subscriptions-transport-ws": "^0.9.17"
|
||||
"subscriptions-transport-ws": "^0.9.18"
|
||||
},
|
||||
"scripts": {
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
@@ -81,10 +84,10 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apollo/react-testing": "^3.1.4",
|
||||
"@apollo/react-testing": "^4.0.0",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-16": "^1.15.2",
|
||||
"enzyme-adapter-react-16": "^1.15.3",
|
||||
"redux-logger": "^3.0.6",
|
||||
"source-map-explorer": "^2.4.2"
|
||||
"source-map-explorer": "^2.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
95
client/public/editor.js
Normal file
95
client/public/editor.js
Normal file
@@ -0,0 +1,95 @@
|
||||
// unlayer.registerPropertyEditor({
|
||||
// name: "field_name",
|
||||
// layout: "bottom",
|
||||
// Widget: unlayer.createWidget({
|
||||
// render(value) {
|
||||
// return `
|
||||
// <input class="field" value=${value} />
|
||||
// `;
|
||||
// },
|
||||
// mount(node, value, updateValue) {
|
||||
// var input = node.getElementsByClassName("field")[0];
|
||||
// input.onchange = function (event) {
|
||||
// updateValue(event.target.value);
|
||||
// };
|
||||
// },
|
||||
// }),
|
||||
// });
|
||||
|
||||
// unlayer.registerTool({
|
||||
// type: "whatever",
|
||||
// category: "contents",
|
||||
// label: "Begin Repeat",
|
||||
// icon: "fa-smile",
|
||||
// values: {},
|
||||
// options: {
|
||||
// default: {
|
||||
// title: null,
|
||||
// },
|
||||
// text: {
|
||||
// title: "Field",
|
||||
// position: 1,
|
||||
// options: {
|
||||
// field: {
|
||||
// label: "Field",
|
||||
// defaultValue: "",
|
||||
// widget: "field_name",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// renderer: {
|
||||
// Viewer: unlayer.createViewer({
|
||||
// render(values) {
|
||||
// console.log(values);
|
||||
// return `
|
||||
// <div style="display: none;">{{#each ${values.field}}}</div>
|
||||
// `;
|
||||
// },
|
||||
// }),
|
||||
// exporters: {
|
||||
// web: function () {},
|
||||
// email: function () {},
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
// unlayer.registerTool({
|
||||
// type: "whatever",
|
||||
// category: "contents",
|
||||
// label: "End Repeat",
|
||||
// icon: "fa-smile",
|
||||
// values: {},
|
||||
// options: {
|
||||
// default: {
|
||||
// title: null,
|
||||
// },
|
||||
// text: {
|
||||
// title: "Field",
|
||||
// position: 1,
|
||||
// options: {
|
||||
// field: {
|
||||
// label: "Field",
|
||||
// defaultValue: "",
|
||||
// widget: "field_name",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// renderer: {
|
||||
// Viewer: unlayer.createViewer({
|
||||
// render(values) {
|
||||
// return `
|
||||
// <div style="display: none;">{{ /each }}</div>
|
||||
// `;
|
||||
// },
|
||||
// }),
|
||||
// exporters: {
|
||||
// web: function () {},
|
||||
// email: function () {},
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
unlayer.registerColumns([2, 2, 2, 2, 2, 2]);
|
||||
unlayer.registerColumns([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
|
||||
85
client/public/render-styles.css
Normal file
85
client/public/render-styles.css
Normal file
@@ -0,0 +1,85 @@
|
||||
/* body {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
line-height: 1.25;
|
||||
} */
|
||||
|
||||
table {
|
||||
border: 1px solid #ccc;
|
||||
border-collapse: collapse;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
table caption {
|
||||
font-size: 1.5em;
|
||||
margin: 0.5em 0 0.75em;
|
||||
}
|
||||
|
||||
table tr {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #ddd;
|
||||
padding: 0.35em;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
padding: 0.625em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table th {
|
||||
font-size: 0.85em;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
table {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
table caption {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
table thead {
|
||||
border: none;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
table tr {
|
||||
border-bottom: 3px solid #ddd;
|
||||
display: block;
|
||||
margin-bottom: 0.625em;
|
||||
}
|
||||
|
||||
table td {
|
||||
border-bottom: 1px solid #ddd;
|
||||
display: block;
|
||||
font-size: 0.8em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table td::before {
|
||||
/*
|
||||
* aria-label has no advantage, it won't be read inside a table
|
||||
content: attr(aria-label);
|
||||
*/
|
||||
content: attr(data-label);
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
table td:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ApolloProvider } from "@apollo/react-common";
|
||||
import { ConfigProvider } from "antd";
|
||||
import enLocale from "antd/es/locale/en_US";
|
||||
import { ApolloLink } from "apollo-boost";
|
||||
import { InMemoryCache } from "apollo-cache-inmemory";
|
||||
import ApolloClient from "apollo-client";
|
||||
@@ -9,18 +11,32 @@ import apolloLogger from "apollo-link-logger";
|
||||
import { RetryLink } from "apollo-link-retry";
|
||||
import { WebSocketLink } from "apollo-link-ws";
|
||||
import { getMainDefinition } from "apollo-utilities";
|
||||
import axios from "axios";
|
||||
import LogRocket from "logrocket";
|
||||
import moment from "moment";
|
||||
import React from "react";
|
||||
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
||||
import { auth } from "../firebase/firebase.utils";
|
||||
import errorLink from "../graphql/apollo-error-handling";
|
||||
import App from "./App";
|
||||
import { ConfigProvider } from "antd";
|
||||
import enLocale from "antd/es/locale/en_US";
|
||||
import moment from "moment";
|
||||
|
||||
moment.locale("en-US");
|
||||
|
||||
axios.interceptors.request.use(
|
||||
async (config) => {
|
||||
if (!config.headers.Authorization) {
|
||||
const token =
|
||||
auth.currentUser && (await auth.currentUser.getIdToken(true));
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
|
||||
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
||||
|
||||
const httpLink = new HttpLink({
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Grid } from "antd";
|
||||
import "antd/dist/antd.css";
|
||||
import React, { lazy, Suspense, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -20,9 +19,7 @@ const ResetPassword = lazy(() =>
|
||||
);
|
||||
const ManagePage = lazy(() => import("../pages/manage/manage.page.container"));
|
||||
const SignInPage = lazy(() => import("../pages/sign-in/sign-in.page"));
|
||||
const Unauthorized = lazy(() =>
|
||||
import("../pages/unauthorized/unauthorized.component")
|
||||
);
|
||||
|
||||
const CsiPage = lazy(() => import("../pages/csi/csi.container.page"));
|
||||
const MobilePaymentContainer = lazy(() =>
|
||||
import("../pages/mobile-payment/mobile-payment.container")
|
||||
@@ -40,8 +37,8 @@ export function App({ checkUserSession, currentUser }) {
|
||||
checkUserSession();
|
||||
}, [checkUserSession]);
|
||||
|
||||
const b = Grid.useBreakpoint();
|
||||
console.log("Breakpoints:", b);
|
||||
//const b = Grid.useBreakpoint();
|
||||
// console.log("Breakpoints:", b);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -52,30 +49,41 @@ export function App({ checkUserSession, currentUser }) {
|
||||
return (
|
||||
<div>
|
||||
<Switch>
|
||||
<ErrorBoundary>
|
||||
<Suspense fallback={<LoadingSpinner message="App.Js Suspense" />}>
|
||||
<Suspense fallback={<LoadingSpinner message="App.Js Suspense" />}>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/" component={LandingPage} />
|
||||
<Route exact path="/unauthorized" component={Unauthorized} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/signin" component={SignInPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/resetpassword" component={ResetPassword} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/csi/:surveyId" component={CsiPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route
|
||||
exact
|
||||
path="/mp/:paymentIs"
|
||||
component={MobilePaymentContainer}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<PrivateRoute
|
||||
isAuthorized={currentUser.authorized}
|
||||
path="/manage"
|
||||
component={ManagePage}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<PrivateRoute
|
||||
isAuthorized={currentUser.authorized}
|
||||
path="/tech"
|
||||
component={TechPageContainer}
|
||||
/>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</ErrorBoundary>
|
||||
</Suspense>
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
42
client/src/App/registerServiceWorker.component.jsx
Normal file
42
client/src/App/registerServiceWorker.component.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { AlertOutlined } from "@ant-design/icons";
|
||||
import { Button, notification } from "antd";
|
||||
import i18n from "i18next";
|
||||
import React from "react";
|
||||
import * as serviceWorker from "../serviceWorker";
|
||||
|
||||
const onServiceWorkerUpdate = (registration) => {
|
||||
console.log("[RSW] onServiceWorkerUpdate", registration);
|
||||
|
||||
const key = `open${Date.now()}`;
|
||||
const btn = (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (registration && registration.waiting) {
|
||||
await registration.unregister();
|
||||
// Makes Workbox call skipWaiting()
|
||||
registration.waiting.postMessage({ type: "SKIP_WAITING" });
|
||||
// Once the service worker is unregistered, we can reload the page to let
|
||||
// the browser download a fresh copy of our app (invalidating the cache)
|
||||
window.location.reload();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{i18n.t("general.actions.refresh")}
|
||||
</Button>
|
||||
);
|
||||
notification.open({
|
||||
icon: <AlertOutlined />,
|
||||
message: i18n.t("general.messages.newversiontitle"),
|
||||
description: i18n.t("general.messages.newversionmessage"),
|
||||
duration: 0,
|
||||
btn,
|
||||
key,
|
||||
});
|
||||
};
|
||||
|
||||
// if (process.env.NODE_ENV === "production") {
|
||||
// console.log("SWR Registering SW...");
|
||||
console.log("Registering Service Worker...");
|
||||
serviceWorker.register({ onUpdate: onServiceWorkerUpdate });
|
||||
// }
|
||||
96
client/src/components/_test/paymentMethod.jsx
Normal file
96
client/src/components/_test/paymentMethod.jsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
PaymentRequestButtonElement,
|
||||
useStripe
|
||||
} from "@stripe/react-stripe-js";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
});
|
||||
|
||||
function Test({ bodyshop, setEmailOptions }) {
|
||||
const stripe = useStripe();
|
||||
|
||||
const [paymentRequest, setPaymentRequest] = useState(null);
|
||||
useEffect(() => {
|
||||
if (stripe) {
|
||||
const pr = stripe.paymentRequest({
|
||||
country: "CA",
|
||||
displayItems: [{ label: "Deductible", amount: 1099 }],
|
||||
currency: "cad",
|
||||
total: {
|
||||
label: "Demo total",
|
||||
amount: 1099,
|
||||
},
|
||||
requestPayerName: true,
|
||||
requestPayerEmail: true,
|
||||
});
|
||||
|
||||
// Check the availability of the Payment Request API.
|
||||
pr.canMakePayment().then((result) => {
|
||||
if (result) {
|
||||
setPaymentRequest(pr);
|
||||
} else {
|
||||
// var details = {
|
||||
// total: { label: "", amount: { currency: "CAD", value: "0.00" } },
|
||||
// };
|
||||
new PaymentRequest(
|
||||
[{ supportedMethods: ["basic-card"] }],
|
||||
{}
|
||||
// details
|
||||
).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [stripe]);
|
||||
|
||||
if (paymentRequest) {
|
||||
return (
|
||||
<div style={{ height: "300px" }}>
|
||||
<PaymentRequestButtonElement options={{ paymentRequest }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
to: ["patrickwf@gmail.com"],
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
name: TemplateList.parts_order_confirmation.key,
|
||||
variables: {
|
||||
id: "a7c2d4e1-f519-42a9-a071-c48cf0f22979",
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
send email
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
logImEXEvent("IMEXEVENT", { somethignArThare: 5 });
|
||||
}}
|
||||
>
|
||||
Log an ImEX Event.
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Test);
|
||||
@@ -1,96 +1,25 @@
|
||||
import {
|
||||
PaymentRequestButtonElement,
|
||||
useStripe
|
||||
} from "@stripe/react-stripe-js";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import Axios from "axios";
|
||||
import React from "react";
|
||||
export default function Test() {
|
||||
const handleQbSignIn = async () => {
|
||||
const result = await Axios.post("/qbo/authorize", { userId: "1234" });
|
||||
console.log("handleQbSignIn -> result", result.data);
|
||||
// window.open(result.data, "_blank", "toolbar=0,location=0,menubar=0");
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
var parameters = "location=1,width=800,height=650";
|
||||
parameters +=
|
||||
",left=" +
|
||||
(window.screen.width - 800) / 2 +
|
||||
",top=" +
|
||||
(window.screen.height - 650) / 2;
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
});
|
||||
|
||||
function Test({ bodyshop, setEmailOptions }) {
|
||||
const stripe = useStripe();
|
||||
|
||||
const [paymentRequest, setPaymentRequest] = useState(null);
|
||||
useEffect(() => {
|
||||
if (stripe) {
|
||||
const pr = stripe.paymentRequest({
|
||||
country: "CA",
|
||||
displayItems: [{ label: "Deductible", amount: 1099 }],
|
||||
currency: "cad",
|
||||
total: {
|
||||
label: "Demo total",
|
||||
amount: 1099,
|
||||
},
|
||||
requestPayerName: true,
|
||||
requestPayerEmail: true,
|
||||
});
|
||||
|
||||
// Check the availability of the Payment Request API.
|
||||
pr.canMakePayment().then((result) => {
|
||||
if (result) {
|
||||
setPaymentRequest(pr);
|
||||
} else {
|
||||
// var details = {
|
||||
// total: { label: "", amount: { currency: "CAD", value: "0.00" } },
|
||||
// };
|
||||
new PaymentRequest(
|
||||
[{ supportedMethods: ["basic-card"] }],
|
||||
{}
|
||||
// details
|
||||
).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [stripe]);
|
||||
|
||||
if (paymentRequest) {
|
||||
return (
|
||||
<div style={{ height: "300px" }}>
|
||||
<PaymentRequestButtonElement options={{ paymentRequest }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// Launch Popup
|
||||
window.open(result.data, "connectPopup", parameters);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
to: ["patrickwf@gmail.com"],
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
name: TemplateList.parts_order_confirmation.key,
|
||||
variables: {
|
||||
id: "a7c2d4e1-f519-42a9-a071-c48cf0f22979",
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
send email
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
logImEXEvent("IMEXEVENT", { somethignArThare: 5 });
|
||||
}}
|
||||
>
|
||||
Log an ImEX Event.
|
||||
</button>
|
||||
<button onClick={handleQbSignIn}>Sign Into Qb.</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Test);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
||||
import { PaymentsExportAllButton } from "../payments-export-all-button/payments-export-all-button.component";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
export default function AccountingPayablesTableComponent({
|
||||
loading,
|
||||
@@ -61,11 +61,13 @@ export default function AccountingPayablesTableComponent({
|
||||
render: (text, record) => {
|
||||
return record.job.owner ? (
|
||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""}`}
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.job.ownr_fn || ""} ${
|
||||
record.job.ownr_ln || ""
|
||||
<span>{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
@@ -168,10 +170,10 @@ export default function AccountingPayablesTableComponent({
|
||||
);
|
||||
}}
|
||||
dataSource={dataSource}
|
||||
size='small'
|
||||
size="small"
|
||||
pagination={{ position: "top", pageSize: 50 }}
|
||||
columns={columns}
|
||||
rowKey='id'
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
onSelectAll: (selected, selectedRows) =>
|
||||
|
||||
@@ -63,10 +63,14 @@ export default function AccountingReceivablesTableComponent({ loading, jobs }) {
|
||||
render: (text, record) => {
|
||||
return record.owner ? (
|
||||
<Link to={"/manage/owners/" + record.owner.id}>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""}`}
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""}`}</span>
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ describe("AllocationsAssignmentComponent component", () => {
|
||||
assignment: {},
|
||||
setAssignment: jest.fn(),
|
||||
visibilityState: [false, jest.fn()],
|
||||
maxHours: 4
|
||||
maxHours: 4,
|
||||
};
|
||||
|
||||
wrapper = mount(<AllocationsAssignmentComponent {...mockProps} />);
|
||||
@@ -27,7 +27,6 @@ describe("AllocationsAssignmentComponent component", () => {
|
||||
|
||||
it("should render a list of employees", () => {
|
||||
const empList = wrapper.find("#employeeSelector");
|
||||
console.log(empList.debug());
|
||||
expect(empList.children()).to.have.lengthOf(2);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export default connect(
|
||||
@@ -18,12 +18,11 @@ export default connect(
|
||||
handleAssignment,
|
||||
assignment,
|
||||
setAssignment,
|
||||
visibilityState
|
||||
visibilityState,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onChange = e => {
|
||||
console.log("e", e);
|
||||
const onChange = (e) => {
|
||||
setAssignment({ ...assignment, employeeid: e });
|
||||
};
|
||||
|
||||
@@ -34,13 +33,14 @@ export default connect(
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: 200 }}
|
||||
placeholder='Select a person'
|
||||
optionFilterProp='children'
|
||||
placeholder="Select a person"
|
||||
optionFilterProp="children"
|
||||
onChange={onChange}
|
||||
filterOption={(input, option) =>
|
||||
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}>
|
||||
{bodyshop.employees.map(emp => (
|
||||
}
|
||||
>
|
||||
{bodyshop.employees.map((emp) => (
|
||||
<Select.Option value={emp.id} key={emp.id}>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
@@ -48,9 +48,10 @@ export default connect(
|
||||
</Select>
|
||||
|
||||
<Button
|
||||
type='primary'
|
||||
type="primary"
|
||||
disabled={!assignment.employeeid}
|
||||
onClick={handleAssignment}>
|
||||
onClick={handleAssignment}
|
||||
>
|
||||
Assign
|
||||
</Button>
|
||||
<Button onClick={() => setVisibility(false)}>Close</Button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Icon from "@ant-design/icons";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { FaCheck, FaCheckDouble } from "react-icons/fa";
|
||||
import { MdDone, MdDoneAll } from "react-icons/md";
|
||||
import {
|
||||
AutoSizer,
|
||||
CellMeasurer,
|
||||
@@ -38,8 +38,9 @@ export default function ChatMessageListComponent({ messages }) {
|
||||
style={style}
|
||||
className={`${
|
||||
messages[index].isoutbound ? "mine messages" : "yours messages"
|
||||
}`}>
|
||||
<div className='message msgmargin'>
|
||||
}`}
|
||||
>
|
||||
<div className="message msgmargin">
|
||||
{MessageRender(messages[index])}
|
||||
{StatusRender(messages[index].status)}
|
||||
</div>
|
||||
@@ -50,7 +51,7 @@ export default function ChatMessageListComponent({ messages }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='chat'>
|
||||
<div className="chat">
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
@@ -73,12 +74,8 @@ export default function ChatMessageListComponent({ messages }) {
|
||||
const MessageRender = (message) => {
|
||||
if (message.image) {
|
||||
return (
|
||||
<a href={message.image_path} target='__blank'>
|
||||
<img
|
||||
alt='Received'
|
||||
className='message-img'
|
||||
src={message.image_path}
|
||||
/>
|
||||
<a href={message.image_path} target="__blank">
|
||||
<img alt="Received" className="message-img" src={message.image_path} />
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
@@ -89,9 +86,9 @@ const MessageRender = (message) => {
|
||||
const StatusRender = (status) => {
|
||||
switch (status) {
|
||||
case "sent":
|
||||
return <Icon component={FaCheck} className='message-icon' />;
|
||||
return <Icon component={MdDone} className="message-icon" />;
|
||||
case "delivered":
|
||||
return <Icon component={FaCheckDouble} className='message-icon' />;
|
||||
return <Icon component={MdDoneAll} className="message-icon" />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -31,12 +31,13 @@ export default function ChatTagRoComponent({
|
||||
onSearch={handleSearchQuery}
|
||||
onSelect={handleInsertTag}
|
||||
placeholder={t("general.labels.search")}
|
||||
onKeyDown={handleKeyDown}>
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
{roOptions.map((item, idx) => (
|
||||
<AutoComplete.Option key={item.id || idx}>
|
||||
{` ${item.ro_number || ""} | ${item.ownr_fn || ""} ${
|
||||
item.ownr_ln || ""
|
||||
}`}
|
||||
} ${item.ownr_co_nm || ""}`}
|
||||
</AutoComplete.Option>
|
||||
))}
|
||||
</AutoComplete>
|
||||
|
||||
@@ -7,12 +7,12 @@ export default function ContractsJobsComponent({
|
||||
loading,
|
||||
data,
|
||||
selectedJob,
|
||||
handleSelect
|
||||
handleSelect,
|
||||
}) {
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" },
|
||||
search: ""
|
||||
search: "",
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
@@ -35,7 +35,7 @@ export default function ContractsJobsComponent({
|
||||
<span>
|
||||
{record.ro_number ? record.ro_number : "EST-" + record.est_number}
|
||||
</span>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
@@ -49,12 +49,14 @@ export default function ContractsJobsComponent({
|
||||
render: (text, record) => {
|
||||
return record.owner ? (
|
||||
<span>
|
||||
{record.ownr_fn} {record.ownr_ln}
|
||||
{record.ownr_fn} {record.ownr_ln} {record.ownr_co_nm || ""}
|
||||
</span>
|
||||
) : (
|
||||
<span>{`${record.ownr_fn} ${record.ownr_ln}`}</span>
|
||||
<span>{`${record.ownr_fn} ${record.ownr_ln} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
@@ -67,7 +69,7 @@ export default function ContractsJobsComponent({
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.status || t("general.labels.na");
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
@@ -79,13 +81,14 @@ export default function ContractsJobsComponent({
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<span>
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc ||
|
||||
""} ${record.v_model_desc || ""}`}
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}
|
||||
</span>
|
||||
) : (
|
||||
t("jobs.errors.novehicle")
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("vehicles.fields.plate_no"),
|
||||
@@ -102,7 +105,7 @@ export default function ContractsJobsComponent({
|
||||
) : (
|
||||
t("general.labels.unknown")
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.clm_no"),
|
||||
@@ -119,8 +122,8 @@ export default function ContractsJobsComponent({
|
||||
) : (
|
||||
t("general.labels.unknown")
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
@@ -131,7 +134,7 @@ export default function ContractsJobsComponent({
|
||||
state.search === ""
|
||||
? data
|
||||
: data.filter(
|
||||
j =>
|
||||
(j) =>
|
||||
(j.est_number || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
@@ -140,6 +143,9 @@ export default function ContractsJobsComponent({
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
@@ -160,7 +166,6 @@ export default function ContractsJobsComponent({
|
||||
.includes(state.search.toLowerCase())
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<Table
|
||||
loading={loading}
|
||||
@@ -168,19 +173,19 @@ export default function ContractsJobsComponent({
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
value={state.search}
|
||||
onChange={e => setState({ ...state, search: e.target.value })}
|
||||
onChange={(e) => setState({ ...state, search: e.target.value })}
|
||||
/>
|
||||
)}
|
||||
size="small"
|
||||
pagination={{ position: "top" }}
|
||||
columns={columns.map(item => ({ ...item }))}
|
||||
columns={columns.map((item) => ({ ...item }))}
|
||||
rowKey="id"
|
||||
dataSource={filteredData}
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
onSelect: handleSelect,
|
||||
type: "radio",
|
||||
selectedRowKeys: [selectedJob]
|
||||
selectedRowKeys: [selectedJob],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -12,13 +12,11 @@ export default function ContractLicenseDecodeButton({ form }) {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [decodedBarcode, setDecodedBarcode] = useState(null);
|
||||
console.log("form", form);
|
||||
|
||||
const handleDecode = (e) => {
|
||||
logImEXEvent("contract_license_decode");
|
||||
setLoading(true);
|
||||
const aamvaParse = aamva.parse(e.currentTarget.value);
|
||||
console.log("AAMVA", aamvaParse);
|
||||
setDecodedBarcode(aamvaParse);
|
||||
setLoading(false);
|
||||
};
|
||||
@@ -61,7 +59,8 @@ export default function ContractLicenseDecodeButton({ form }) {
|
||||
okText={t("contracts.actions.senddltoform")}
|
||||
onOk={handleInsertForm}
|
||||
okButtonProps={{ disabled: !!!decodedBarcode }}
|
||||
onCancel={handleCancel}>
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<div>
|
||||
<div>
|
||||
<Input
|
||||
|
||||
@@ -3,10 +3,7 @@ import { Select } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const { Option } = Select;
|
||||
|
||||
const ContractStatusComponent = (
|
||||
{ value = "contracts.status.new", onChange },
|
||||
ref
|
||||
) => {
|
||||
const ContractStatusComponent = ({ value, onChange }, ref) => {
|
||||
const [option, setOption] = useState(value);
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@ import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
|
||||
import FormDatePicker from '../form-date-picker/form-date-picker.component';
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
|
||||
export default function CourtesyCarCreateFormComponent({ form }) {
|
||||
export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<Button type="primary" htmlType="submit">
|
||||
<Button type="primary" loading={saveLoading} htmlType="submit">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<div className="imex-flex-row__grow imex-flex-row__margin-large">
|
||||
|
||||
@@ -60,11 +60,13 @@ export default function CsiResponseListPaginated({
|
||||
render: (text, record) => {
|
||||
return record.owner ? (
|
||||
<Link to={"/manage/owners/" + record.owner.id}>
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""}`}
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.job.ownr_fn || ""} ${
|
||||
record.job.ownr_ln || ""
|
||||
<span>{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -38,7 +38,6 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
export function DashboardGridComponent({ currentUser, bodyshop }) {
|
||||
const { loading, error, data } = useQuery(QUERY_DASHBOARD_DETAILS);
|
||||
console.log("DashboardGridComponent -> data", data)
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
layout: bodyshop.associations[0].user.dashboardlayout || [
|
||||
@@ -70,7 +69,6 @@ export function DashboardGridComponent({ currentUser, bodyshop }) {
|
||||
const idxToRemove = state.layout.findIndex((i) => i.i === key);
|
||||
const newLayout = state.layout;
|
||||
newLayout.splice(idxToRemove, 1);
|
||||
console.log(newLayout);
|
||||
handleLayoutChange(newLayout);
|
||||
};
|
||||
|
||||
@@ -100,14 +98,15 @@ export function DashboardGridComponent({ currentUser, bodyshop }) {
|
||||
<Menu.Item
|
||||
key={key}
|
||||
value={key}
|
||||
disabled={existingLayoutKeys.includes(key)}>
|
||||
disabled={existingLayoutKeys.includes(key)}
|
||||
>
|
||||
{componentList[key].label}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -115,12 +114,13 @@ export function DashboardGridComponent({ currentUser, bodyshop }) {
|
||||
<Button>{t("dashboard.actions.addcomponent")}</Button>
|
||||
</Dropdown>
|
||||
<ResponsiveReactGridLayout
|
||||
className='layout'
|
||||
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%'
|
||||
width="100%"
|
||||
onLayoutChange={handleLayoutChange}
|
||||
onBreakpointChange={onBreakpointChange}>
|
||||
onBreakpointChange={onBreakpointChange}
|
||||
>
|
||||
{state.layout.map((item, index) => {
|
||||
const TheComponent = componentList[item.i].component;
|
||||
return (
|
||||
@@ -139,8 +139,8 @@ export function DashboardGridComponent({ currentUser, bodyshop }) {
|
||||
onClick={() => handleRemoveComponent(item.i)}
|
||||
/>
|
||||
<TheComponent
|
||||
className='dashboard-card'
|
||||
size='small'
|
||||
className="dashboard-card"
|
||||
size="small"
|
||||
style={{ height: "100%", width: "100%" }}
|
||||
/>
|
||||
</LoadingSkeleton>
|
||||
|
||||
@@ -62,7 +62,6 @@ export function EmailOverlayContainer({
|
||||
};
|
||||
attachments.push(t);
|
||||
});
|
||||
console.log("messageOptions", messageOptions);
|
||||
|
||||
setSending(true);
|
||||
try {
|
||||
|
||||
@@ -5,7 +5,7 @@ const { Option } = Select;
|
||||
//To be used as a form element only.
|
||||
|
||||
const EmployeeSearchSelect = (
|
||||
{ value, onChange, options, onSelect, onBlur },
|
||||
{ value, onChange, options, onSelect, onBlur, ...restProps },
|
||||
ref
|
||||
) => {
|
||||
const [option, setOption] = useState(value);
|
||||
@@ -27,6 +27,7 @@ const EmployeeSearchSelect = (
|
||||
optionFilterProp="search"
|
||||
onSelect={onSelect}
|
||||
onBlur={onBlur}
|
||||
{...restProps}
|
||||
>
|
||||
{options
|
||||
? options.map((o) => (
|
||||
|
||||
@@ -21,7 +21,6 @@ export default function FormsFieldChanged({ form }) {
|
||||
<Prompt
|
||||
when={true}
|
||||
message={(location) => {
|
||||
//console.log("location", location);
|
||||
if (loc.pathname === location.pathname) return false;
|
||||
return t("general.messages.unsavedchangespopup");
|
||||
}}
|
||||
@@ -45,7 +44,7 @@ export default function FormsFieldChanged({ form }) {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
return null;
|
||||
return <div style={{ display: "none" }}></div>;
|
||||
}}
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
@@ -106,7 +106,7 @@ export default function GlobalSearch() {
|
||||
vehicle.v_model_yr || ""
|
||||
} ${vehicle.v_make_desc || ""} ${
|
||||
vehicle.v_model_desc || ""
|
||||
}`}</span>
|
||||
} - ${vehicle.plate_no} - ${vehicle.v_vin}`}</span>
|
||||
</div>
|
||||
</Link>
|
||||
),
|
||||
|
||||
@@ -1,30 +1,40 @@
|
||||
import Icon, {
|
||||
ClockCircleFilled,
|
||||
CarFilled,
|
||||
ClockCircleFilled,
|
||||
DollarCircleFilled,
|
||||
FileAddFilled,
|
||||
FileFilled,
|
||||
GlobalOutlined,
|
||||
HomeFilled,
|
||||
ImportOutlined,
|
||||
LineChartOutlined,
|
||||
ScheduleOutlined,
|
||||
TeamOutlined,
|
||||
UnorderedListOutlined,
|
||||
UserOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Avatar, Col, Layout, Menu, Row } from "antd";
|
||||
import { Avatar, Layout, Menu } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaCalendarAlt, FaCarCrash, FaCreditCard } from "react-icons/fa";
|
||||
import { BsKanban } from "react-icons/bs";
|
||||
import {
|
||||
FaCalendarAlt,
|
||||
FaCarCrash,
|
||||
FaCreditCard,
|
||||
FaFileInvoiceDollar,
|
||||
} from "react-icons/fa";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectRecentItems } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { signOutStart } from "../../redux/user/user.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import "./header.styles.scss";
|
||||
import GlobalSearch from "../global-search/global-search.component";
|
||||
import { selectRecentItems } from "../../redux/application/application.selectors";
|
||||
import "./header.styles.scss";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -42,30 +52,6 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
signOutStart: () => dispatch(signOutStart()),
|
||||
});
|
||||
|
||||
const logoSpan = {
|
||||
xs: {
|
||||
span: 0,
|
||||
},
|
||||
sm: { span: 0 },
|
||||
md: {
|
||||
span: 0,
|
||||
},
|
||||
lg: {
|
||||
span: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const menuSpan = {
|
||||
md: {
|
||||
span: 24,
|
||||
//offset: 1,
|
||||
},
|
||||
lg: {
|
||||
span: 21,
|
||||
offset: 1,
|
||||
},
|
||||
};
|
||||
|
||||
function Header({
|
||||
bodyshop,
|
||||
handleMenuClick,
|
||||
@@ -81,278 +67,276 @@ function Header({
|
||||
|
||||
return (
|
||||
<Header>
|
||||
<Row>
|
||||
<Col {...logoSpan}>
|
||||
<img
|
||||
className="header-shop-logo"
|
||||
alt={bodyshop ? bodyshop.shopname : "ImEX Online Logo"}
|
||||
src={
|
||||
bodyshop && bodyshop.logo_img_path
|
||||
? bodyshop.logo_img_path
|
||||
: "./logo192.png"
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...menuSpan}>
|
||||
<Menu
|
||||
mode="horizontal"
|
||||
theme="dark"
|
||||
className="header-main-menu"
|
||||
selectedKeys={["home"]}
|
||||
onClick={handleMenuClick}
|
||||
<Menu
|
||||
mode="horizontal"
|
||||
theme="dark"
|
||||
className="header-main-menu"
|
||||
selectedKeys={["home"]}
|
||||
onClick={handleMenuClick}
|
||||
>
|
||||
<Menu.Item key="home">
|
||||
<Link to="/manage">
|
||||
<HomeFilled />
|
||||
{t("menus.header.home")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="schedule">
|
||||
<Link to="/manage/schedule">
|
||||
<Icon component={FaCalendarAlt} />
|
||||
{t("menus.header.schedule")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<Icon component={FaCarCrash} />
|
||||
<span>{t("menus.header.jobs")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="activejobs">
|
||||
<FileFilled />
|
||||
<Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="availablejobs">
|
||||
<Link to="/manage/available">
|
||||
<ImportOutlined /> {t("menus.header.availablejobs")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item key="alljobs">
|
||||
<UnorderedListOutlined />
|
||||
<Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
|
||||
<Menu.Item key="productionlist">
|
||||
<Link to="/manage/production/list">
|
||||
<ScheduleOutlined />
|
||||
{t("menus.header.productionlist")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="productionboard">
|
||||
<Link to="/manage/production/board">
|
||||
<Icon component={BsKanban} />
|
||||
{t("menus.header.productionboard")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
|
||||
<Menu.Item key="scoreboard">
|
||||
<LineChartOutlined />
|
||||
<Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<UserOutlined />
|
||||
<span>{t("menus.header.customers")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="owners">
|
||||
<Link to="/manage/owners">
|
||||
<TeamOutlined />
|
||||
{t("menus.header.owners")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="vehicles">
|
||||
<Link to="/manage/vehicles">
|
||||
<CarFilled />
|
||||
{t("menus.header.vehicles")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<CarFilled />
|
||||
<span>{t("menus.header.courtesycars")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="courtesycarsall">
|
||||
<Link to="/manage/courtesycars">
|
||||
<CarFilled />
|
||||
{t("menus.header.courtesycars-all")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="contracts">
|
||||
<Link to="/manage/courtesycars/contracts">
|
||||
<FileFilled />
|
||||
{t("menus.header.courtesycars-contracts")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="newcontract">
|
||||
<Link to="/manage/courtesycars/contracts/new">
|
||||
<FileAddFilled />
|
||||
{t("menus.header.courtesycars-newcontract")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<DollarCircleFilled />
|
||||
<span>{t("menus.header.accounting")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="invoices">
|
||||
<Link to="/manage/invoices">{t("menus.header.invoices")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key="enterinvoices"
|
||||
onClick={() => {
|
||||
setInvoiceEnterContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Menu.Item key="home">
|
||||
<Link to="/manage">
|
||||
<HomeFilled />
|
||||
{t("menus.header.home")}
|
||||
<Icon component={FaFileInvoiceDollar} />
|
||||
{t("menus.header.enterinvoices")}
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item key="allpayments">
|
||||
<Link to="/manage/payments">{t("menus.header.allpayments")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key="enterpayments"
|
||||
onClick={() => {
|
||||
setPaymentContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Icon component={FaCreditCard} />
|
||||
{t("menus.header.enterpayment")}
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
|
||||
<Menu.Item key="timetickets">
|
||||
<Link to="/manage/timetickets">
|
||||
{t("menus.header.timetickets")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key="entertimetickets"
|
||||
onClick={() => {
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("menus.header.entertimeticket")}
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
|
||||
<Menu.SubMenu title={t("menus.header.export")}>
|
||||
<Menu.Item key="receivables">
|
||||
<Link to="/manage/accounting/receivables">
|
||||
{t("menus.header.accounting-receivables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<Icon component={FaCarCrash} />
|
||||
<span>{t("menus.header.jobs")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="schedule">
|
||||
<Link to="/manage/schedule">
|
||||
<Icon component={FaCalendarAlt} />
|
||||
{t("menus.header.schedule")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="productionlist">
|
||||
<Link to="/manage/production/list">
|
||||
<Icon component={FaCalendarAlt} />
|
||||
{t("menus.header.productionlist")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="productionboard">
|
||||
<Link to="/manage/production/board">
|
||||
{t("menus.header.productionboard")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="scoreboard">
|
||||
<Link to="/manage/scoreboard">
|
||||
{t("menus.header.scoreboard")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="activejobs">
|
||||
<Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="alljobs">
|
||||
<Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="availablejobs">
|
||||
<Link to="/manage/available">
|
||||
{t("menus.header.availablejobs")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu title={t("menus.header.customers")}>
|
||||
<Menu.Item key="owners">
|
||||
<Link to="/manage/owners">
|
||||
<TeamOutlined />
|
||||
{t("menus.header.owners")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="vehicles">
|
||||
<Link to="/manage/vehicles">
|
||||
<CarFilled />
|
||||
{t("menus.header.vehicles")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<CarFilled />
|
||||
<span>{t("menus.header.courtesycars")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="courtesycarsall">
|
||||
<Link to="/manage/courtesycars">
|
||||
<CarFilled />
|
||||
{t("menus.header.courtesycars-all")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="contracts">
|
||||
<Link to="/manage/courtesycars/contracts">
|
||||
<FileFilled />
|
||||
{t("menus.header.courtesycars-contracts")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="newcontract">
|
||||
<Link to="/manage/courtesycars/contracts/new">
|
||||
<FileAddFilled />
|
||||
{t("menus.header.courtesycars-newcontract")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<DollarCircleFilled />
|
||||
<span>{t("menus.header.accounting")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item
|
||||
key="enterpayments"
|
||||
onClick={() => {
|
||||
setPaymentContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Icon component={FaCreditCard} />
|
||||
{t("menus.header.enterpayment")}
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item
|
||||
key="enterinvoices"
|
||||
onClick={() => {
|
||||
setInvoiceEnterContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("menus.header.enterinvoices")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key="invoices">
|
||||
<Link to="/manage/invoices">{t("menus.header.invoices")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="timetickets">
|
||||
<Link to="/manage/timetickets">
|
||||
{t("menus.header.timetickets")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key="entertimetickets"
|
||||
onClick={() => {
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("menus.header.entertimeticket")}
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.SubMenu title={t("menus.header.export")}>
|
||||
<Menu.Item key="receivables">
|
||||
<Link to="/manage/accounting/receivables">
|
||||
{t("menus.header.accounting-receivables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="payables">
|
||||
<Link to="/manage/accounting/payables">
|
||||
{t("menus.header.accounting-payables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="payments">
|
||||
<Link to="/manage/accounting/payments">
|
||||
{t("menus.header.accounting-payments")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
|
||||
<Menu.Item key="allpayments">
|
||||
<Link to="/manage/payments">
|
||||
{t("menus.header.allpayments")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu title={t("menus.header.shop")}>
|
||||
<Menu.Item key="shop">
|
||||
<Link to="/manage/shop">{t("menus.header.shop_config")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="shop-templates">
|
||||
<Link to="/manage/shop/templates">
|
||||
{t("menus.header.shop_templates")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="shop-vendors">
|
||||
<Link to="/manage/shop/vendors">
|
||||
{t("menus.header.shop_vendors")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="shop-csi">
|
||||
<Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.Item>
|
||||
<GlobalSearch />
|
||||
<Menu.Item key="payables">
|
||||
<Link to="/manage/accounting/payables">
|
||||
{t("menus.header.accounting-payables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu title={<ClockCircleFilled />}>
|
||||
{recentItems.map((i, idx) => (
|
||||
<Menu.Item key={idx}>
|
||||
<Link to={i.url}>{i.label}</Link>
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<div>
|
||||
{currentUser.photoURL ? (
|
||||
<Avatar
|
||||
src={currentUser.photoURL}
|
||||
style={{
|
||||
margin: "10px",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Avatar
|
||||
style={{
|
||||
backgroundColor: "#87d068",
|
||||
margin: "10px",
|
||||
}}
|
||||
icon={<UserOutlined />}
|
||||
/>
|
||||
)}
|
||||
<Menu.Item key="payments">
|
||||
<Link to="/manage/accounting/payments">
|
||||
{t("menus.header.accounting-payments")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu title={t("menus.header.shop")}>
|
||||
<Menu.Item key="shop">
|
||||
<Link to="/manage/shop">{t("menus.header.shop_config")}</Link>
|
||||
</Menu.Item>
|
||||
|
||||
{currentUser.displayName || t("general.labels.unknown")}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Menu.Item danger onClick={() => signOutStart()}>
|
||||
{t("user.actions.signout")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key="shiftclock">
|
||||
<Link to="/manage/shiftclock">
|
||||
{t("menus.header.shiftclock")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<Link to="/manage/profile">
|
||||
{t("menus.currentuser.profile")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<GlobalOutlined />
|
||||
<span>{t("menus.currentuser.languageselector")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item actiontype="lang-select" key="en-US">
|
||||
{t("general.languages.english")}
|
||||
</Menu.Item>
|
||||
<Menu.Item actiontype="lang-select" key="fr-CA">
|
||||
{t("general.languages.french")}
|
||||
</Menu.Item>
|
||||
<Menu.Item actiontype="lang-select" key="es-MX">
|
||||
{t("general.languages.spanish")}
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
</Menu.SubMenu>
|
||||
</Menu>
|
||||
</Col>
|
||||
</Row>
|
||||
<Menu.Item key="shop-templates">
|
||||
<Link to="/manage/shop/templates">
|
||||
{t("menus.header.shop_templates")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item key="shop-vendors">
|
||||
<Link to="/manage/shop/vendors">
|
||||
{t("menus.header.shop_vendors")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="shop-csi">
|
||||
<Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.Item>
|
||||
<GlobalSearch />
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu title={<ClockCircleFilled />}>
|
||||
{recentItems.map((i, idx) => (
|
||||
<Menu.Item key={idx}>
|
||||
<Link to={i.url}>{i.label}</Link>
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<div>
|
||||
{currentUser.photoURL ? (
|
||||
<Avatar
|
||||
src={currentUser.photoURL}
|
||||
style={{
|
||||
margin: "10px",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Avatar
|
||||
style={{
|
||||
backgroundColor: "#87d068",
|
||||
margin: "10px",
|
||||
}}
|
||||
icon={<UserOutlined />}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentUser.displayName || t("general.labels.unknown")}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Menu.Item danger onClick={() => signOutStart()}>
|
||||
{t("user.actions.signout")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key="shiftclock">
|
||||
<Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<GlobalOutlined />
|
||||
<span>{t("menus.currentuser.languageselector")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item actiontype="lang-select" key="en-US">
|
||||
{t("general.languages.english")}
|
||||
</Menu.Item>
|
||||
<Menu.Item actiontype="lang-select" key="fr-CA">
|
||||
{t("general.languages.french")}
|
||||
</Menu.Item>
|
||||
<Menu.Item actiontype="lang-select" key="es-MX">
|
||||
{t("general.languages.spanish")}
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
</Menu.SubMenu>
|
||||
</Menu>
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,13 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
|
||||
delete il.__typename;
|
||||
updates.push(
|
||||
updateInvoiceLine({
|
||||
variables: { invoicelineId: il.id, invoiceLine: il },
|
||||
variables: {
|
||||
invoicelineId: il.id,
|
||||
invoiceLine: {
|
||||
...il,
|
||||
joblineid: il.joblineid === "noline" ? null : il.joblineid,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -38,8 +38,6 @@ function InvoiceEnterModalContainer({
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleFinish = (values) => {
|
||||
console.log("handleFinish -> values", values);
|
||||
|
||||
setLoading(true);
|
||||
const { upload, ...remainingValues } = values;
|
||||
insertInvoice({
|
||||
@@ -48,11 +46,6 @@ function InvoiceEnterModalContainer({
|
||||
Object.assign({}, remainingValues, {
|
||||
invoicelines: {
|
||||
data: remainingValues.invoicelines.map((i) => {
|
||||
console.log(
|
||||
"Initial insert value",
|
||||
i.joblineid,
|
||||
i.joblineid === "noline"
|
||||
);
|
||||
return {
|
||||
...i,
|
||||
joblineid: i.joblineid === "noline" ? null : i.joblineid,
|
||||
@@ -127,6 +120,10 @@ function InvoiceEnterModalContainer({
|
||||
if (enterAgain) form.submit();
|
||||
}, [enterAgain, form]);
|
||||
|
||||
useEffect(() => {
|
||||
if (invoiceEnterModal.visible) form.resetFields();
|
||||
}, [invoiceEnterModal.visible, form]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t("invoices.labels.new")}
|
||||
@@ -165,6 +162,7 @@ function InvoiceEnterModalContainer({
|
||||
setEnterAgain(false);
|
||||
}}
|
||||
initialValues={{
|
||||
...invoiceEnterModal.context.invoice,
|
||||
jobid:
|
||||
(invoiceEnterModal.context.job &&
|
||||
invoiceEnterModal.context.job.id) ||
|
||||
@@ -172,7 +170,6 @@ function InvoiceEnterModalContainer({
|
||||
federal_tax_rate: bodyshop.invoice_tax_rates.federal_tax_rate || 0,
|
||||
state_tax_rate: bodyshop.invoice_tax_rates.state_tax_rate || 0,
|
||||
local_tax_rate: bodyshop.invoice_tax_rates.local_tax_rate || 0,
|
||||
...invoiceEnterModal.context.invoice,
|
||||
}}
|
||||
>
|
||||
<InvoiceFormContainer form={form} />
|
||||
|
||||
@@ -42,7 +42,6 @@ export function InvoiceExportAllButton({
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
@@ -121,7 +120,8 @@ export function InvoiceExportAllButton({
|
||||
onClick={handleQbxml}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
type='dashed'>
|
||||
type="dashed"
|
||||
>
|
||||
{t("jobs.actions.exportselected")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,6 @@ export function InvoiceExportButton({
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
@@ -118,7 +117,8 @@ export function InvoiceExportButton({
|
||||
onClick={handleQbxml}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
type='dashed'>
|
||||
type="dashed"
|
||||
>
|
||||
{t("jobs.actions.export")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -159,7 +159,6 @@ export default function InvoiceFormComponent({
|
||||
style={{ display: invoiceEdit ? "none" : null }}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={(e) => {
|
||||
console.log("Upload event:", e);
|
||||
if (Array.isArray(e)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import InvoiceLineSearchSelect from "../invoice-line-search-select/invoice-line-search-select.component";
|
||||
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
export default function InvoiceEnterModalLinesComponent({
|
||||
lineData,
|
||||
discount,
|
||||
@@ -16,7 +16,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
|
||||
return (
|
||||
<Form.List name="invoicelines">
|
||||
{(fields, { add, remove }) => {
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div className="invoice-form-lines-wrapper">
|
||||
{fields.map((field, index) => (
|
||||
@@ -197,6 +197,11 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
<FormListMoveArrows
|
||||
move={move}
|
||||
index={index}
|
||||
total={fields.length}
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
))}
|
||||
|
||||
@@ -208,19 +208,12 @@ export function InvoicesListTableComponent({
|
||||
];
|
||||
|
||||
const handleOnInvoiceRowclick = (selectedRows) => {
|
||||
console.log("selectedRows", selectedRows);
|
||||
console.log("record.id", record.id);
|
||||
setSelectedInvoiceLinesByInvoice({
|
||||
...selectedInvoiceLinesByInvoice,
|
||||
[record.id]: selectedRows.map((r) => r.id),
|
||||
});
|
||||
};
|
||||
|
||||
console.log(
|
||||
"selectedInvoiceLinesByInvoice[record.id]",
|
||||
selectedInvoiceLinesByInvoice[record.id]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography.Title level={3}>{`${t("invoices.fields.invoice_number")} ${
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Button, notification } from "antd";
|
||||
import Axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useMutation } from "react-apollo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function JobCalculateTotals({ bodyshop, job }) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
|
||||
const handleCalculate = async () => {
|
||||
setLoading(true);
|
||||
const newTotals = (
|
||||
await Axios.post("/job/totals", {
|
||||
job: job,
|
||||
shoprates: bodyshop.shoprates,
|
||||
})
|
||||
).data;
|
||||
|
||||
const result = await updateJob({
|
||||
refetchQueries: ["GET_JOB_BY_PK"],
|
||||
awaitRefetchQueries: true,
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
job_totals: newTotals,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({ message: t("jobs.successes.updated") });
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.updating", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button loading={loading} onClick={handleCalculate}>
|
||||
{t("jobs.actions.recalculate")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(JobCalculateTotals);
|
||||
@@ -19,12 +19,7 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
|
||||
const jobLineTotalsByProfitCenter = job.joblines.reduce(
|
||||
(acc, val) => {
|
||||
const laborProfitCenter = defaultProfits[val.mod_lbr_ty];
|
||||
if (!!!laborProfitCenter)
|
||||
console.log(
|
||||
"Unknown cost/profit center mapping for labor.",
|
||||
val.mod_lbr_ty
|
||||
);
|
||||
const laborProfitCenter = defaultProfits[val.mod_lbr_ty] || "?";
|
||||
|
||||
const rateName = `rate_${(val.mod_lbr_ty || "").toLowerCase()}`;
|
||||
const laborAmount = Dinero({
|
||||
@@ -36,7 +31,7 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
laborAmount
|
||||
);
|
||||
|
||||
const partsProfitCenter = defaultProfits[val.part_type];
|
||||
const partsProfitCenter = defaultProfits[val.part_type] || "?";
|
||||
if (!!!partsProfitCenter)
|
||||
console.log(
|
||||
"Unknown cost/profit center mapping for parts.",
|
||||
@@ -79,10 +74,31 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
{}
|
||||
);
|
||||
|
||||
const ticketTotalsByProfitCenter = job.timetickets.reduce(
|
||||
(ticket_acc, ticket_val) => {
|
||||
//At the invoice level.
|
||||
if (!!!ticket_acc[ticket_val.cost_center])
|
||||
ticket_acc[ticket_val.cost_center] = Dinero();
|
||||
|
||||
ticket_acc[ticket_val.cost_center] = ticket_acc[
|
||||
ticket_val.cost_center
|
||||
].add(
|
||||
Dinero({
|
||||
amount: Math.round((ticket_val.rate || 0) * 100),
|
||||
}).multiply(ticket_val.actualhrs || 0)
|
||||
);
|
||||
|
||||
return ticket_acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const summaryData = {
|
||||
totalLaborSales: Dinero({ amount: 0 }),
|
||||
totalPartsSales: Dinero({ amount: 0 }),
|
||||
totalSales: Dinero({ amount: 0 }),
|
||||
totalLaborCost: Dinero({ amount: 0 }),
|
||||
totalPartsCost: Dinero({ amount: 0 }),
|
||||
totalCost: Dinero({ amount: 0 }),
|
||||
gpdollars: Dinero({ amount: 0 }),
|
||||
gppercent: null,
|
||||
@@ -95,7 +111,15 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
jobLineTotalsByProfitCenter.labor[ccVal] || Dinero({ amount: 0 });
|
||||
const sale_parts =
|
||||
jobLineTotalsByProfitCenter.parts[ccVal] || Dinero({ amount: 0 });
|
||||
const cost = invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
|
||||
|
||||
const cost_labor =
|
||||
ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
|
||||
const cost_parts =
|
||||
invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
|
||||
|
||||
const cost = (
|
||||
invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })
|
||||
).add(ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 }));
|
||||
const totalSales = sale_labor.add(sale_parts);
|
||||
const gpdollars = totalSales.subtract(cost);
|
||||
const gppercent = (
|
||||
@@ -115,6 +139,8 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
summaryData.totalSales = summaryData.totalSales
|
||||
.add(sale_labor)
|
||||
.add(sale_parts);
|
||||
summaryData.totalLaborCost = summaryData.totalLaborCost.add(cost_labor);
|
||||
summaryData.totalPartsCost = summaryData.totalPartsCost.add(cost_parts);
|
||||
summaryData.totalCost = summaryData.totalCost.add(cost);
|
||||
|
||||
return {
|
||||
@@ -122,6 +148,8 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
cost_center: ccVal,
|
||||
sale_labor: sale_labor && sale_labor.toFormat(),
|
||||
sale_parts: sale_parts && sale_parts.toFormat(),
|
||||
cost_parts: cost_parts && cost_parts.toFormat(),
|
||||
cost_labor: cost_labor && cost_labor.toFormat(),
|
||||
cost: cost && cost.toFormat(),
|
||||
gpdollars: gpdollars.toFormat(),
|
||||
gppercent: gppercentFormatted,
|
||||
@@ -143,8 +171,6 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
summaryData.gppercentFormatted = summaryData.gppercent;
|
||||
}
|
||||
|
||||
console.log("JobCostingModalComponent -> summaryData", summaryData);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<JobCostingStatistics job={job} summaryData={summaryData} />
|
||||
|
||||
@@ -40,12 +40,20 @@ export default function JobCostingPartsTable({ job, data }) {
|
||||
state.sortedInfo.columnKey === "sale_parts" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.cost"),
|
||||
dataIndex: "cost",
|
||||
key: "cost",
|
||||
sorter: (a, b) => a.cost - b.cost,
|
||||
title: t("jobs.labels.cost_labor"),
|
||||
dataIndex: "cost_labor",
|
||||
key: "cost_labor",
|
||||
sorter: (a, b) => a.cost_labor - b.cost_labor,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "cost" && state.sortedInfo.order,
|
||||
state.sortedInfo.columnKey === "cost_labor" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.cost_parts"),
|
||||
dataIndex: "cost_parts",
|
||||
key: "cost_parts",
|
||||
sorter: (a, b) => a.cost_parts - b.cost_parts,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "cost_parts" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.gpdollars"),
|
||||
|
||||
@@ -20,6 +20,14 @@ export default function JobCostingStatistics({ job, summaryData }) {
|
||||
value={summaryData.totalSales.toFormat()}
|
||||
title={t("jobs.labels.total_sales")}
|
||||
/>
|
||||
<Statistic
|
||||
value={summaryData.totalLaborCost.toFormat()}
|
||||
title={t("jobs.labels.cost_labor")}
|
||||
/>
|
||||
<Statistic
|
||||
value={summaryData.totalPartsCost.toFormat()}
|
||||
title={t("jobs.labels.cost_parts")}
|
||||
/>
|
||||
<Statistic
|
||||
value={summaryData.totalCost.toFormat()}
|
||||
title={t("jobs.labels.total_cost")}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -145,7 +145,7 @@ export function JobDetailCards({ setPrintCenterContext }) {
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...colBreakPoints}>
|
||||
<Col span={24}>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
|
||||
@@ -9,11 +9,10 @@ export default function JobDetailCardsDamageComponent({ loading, data }) {
|
||||
return (
|
||||
<CardTemplate loading={loading} title={t("jobs.labels.cards.damage")}>
|
||||
{area_of_damage ? (
|
||||
<Car
|
||||
dmg1={area_of_damage.impact1 || null}
|
||||
dmg2={area_of_damage.impact2 || null}
|
||||
/>
|
||||
) : t("jobs.errors.nodamage")}
|
||||
<Car dmg1={area_of_damage.impact1} dmg2={area_of_damage.impact2} />
|
||||
) : (
|
||||
t("jobs.errors.nodamage")
|
||||
)}
|
||||
</CardTemplate>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function JobDetailCardsNotesComponent({ loading, data }) {
|
||||
<CardTemplate
|
||||
loading={loading}
|
||||
title={t("jobs.labels.cards.notes")}
|
||||
extraLink={`/manage/jobs/${data.id}?notes`}
|
||||
extraLink={`/manage/jobs/${data.id}?tab=notes`}
|
||||
>
|
||||
{data ? (
|
||||
<Container>
|
||||
@@ -25,7 +25,7 @@ export default function JobDetailCardsNotesComponent({ loading, data }) {
|
||||
size="small"
|
||||
bordered
|
||||
dataSource={data.notes}
|
||||
renderItem={item => (
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
{item.critical ? (
|
||||
<EyeInvisibleFilled style={{ margin: 4, color: "red" }} />
|
||||
|
||||
@@ -1,14 +1,141 @@
|
||||
import React from "react";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Pie, PieChart, Sector } from "recharts";
|
||||
import CardTemplate from "./job-detail-cards.template.component";
|
||||
|
||||
export default function JobDetailCardsPartsComponent({ loading, data }) {
|
||||
const { t } = useTranslation();
|
||||
const { joblines_status } = data;
|
||||
// console.log(
|
||||
// "JobDetailCardsPartsComponent -> joblines_stats",
|
||||
// joblines_status
|
||||
// );
|
||||
|
||||
const memoizedData = useMemo(() => Calculatedata(joblines_status), [
|
||||
joblines_status,
|
||||
]);
|
||||
|
||||
const [state, setState] = useState({ activeIndex: 0 });
|
||||
|
||||
const onPieEnter = (data, index) => {
|
||||
setState({
|
||||
activeIndex: index,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
|
||||
Placeholder piechart.
|
||||
<PieChart width={400} height={400}>
|
||||
<Pie
|
||||
activeIndex={state.activeIndex}
|
||||
activeShape={renderActiveShape}
|
||||
data={memoizedData}
|
||||
cx={200}
|
||||
cy={200}
|
||||
innerRadius={60}
|
||||
outerRadius={80}
|
||||
fill="#8884d8"
|
||||
dataKey="value"
|
||||
onMouseEnter={onPieEnter}
|
||||
/>
|
||||
</PieChart>
|
||||
</CardTemplate>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Calculatedata = (data) => {
|
||||
if (data.length > 0) {
|
||||
const statusMapping = {};
|
||||
data.map((i) => {
|
||||
if (!statusMapping[i.status])
|
||||
statusMapping[i.status] = { name: i.status || "No Status*", value: 0 };
|
||||
statusMapping[i.status].value = statusMapping[i.status].value + i.count;
|
||||
return null;
|
||||
});
|
||||
return Object.keys(statusMapping).map((key) => {
|
||||
return statusMapping[key];
|
||||
});
|
||||
} else {
|
||||
return [
|
||||
{ name: "Group A", value: 400 },
|
||||
{ name: "Group B", value: 300 },
|
||||
{ name: "Group C", value: 300 },
|
||||
{ name: "Group D", value: 200 },
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
const renderActiveShape = (props) => {
|
||||
const RADIAN = Math.PI / 180;
|
||||
const {
|
||||
cx,
|
||||
cy,
|
||||
midAngle,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
fill,
|
||||
payload,
|
||||
percent,
|
||||
value,
|
||||
} = props;
|
||||
const sin = Math.sin(-RADIAN * midAngle);
|
||||
const cos = Math.cos(-RADIAN * midAngle);
|
||||
const sx = cx + (outerRadius + 10) * cos;
|
||||
const sy = cy + (outerRadius + 10) * sin;
|
||||
const mx = cx + (outerRadius + 30) * cos;
|
||||
const my = cy + (outerRadius + 30) * sin;
|
||||
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
||||
const ey = my;
|
||||
const textAnchor = cos >= 0 ? "start" : "end";
|
||||
|
||||
return (
|
||||
<g>
|
||||
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>
|
||||
{payload.name}
|
||||
</text>
|
||||
<Sector
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
innerRadius={innerRadius}
|
||||
outerRadius={outerRadius}
|
||||
startAngle={startAngle}
|
||||
endAngle={endAngle}
|
||||
fill={fill}
|
||||
/>
|
||||
<Sector
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
startAngle={startAngle}
|
||||
endAngle={endAngle}
|
||||
innerRadius={outerRadius + 6}
|
||||
outerRadius={outerRadius + 10}
|
||||
fill={fill}
|
||||
/>
|
||||
<path
|
||||
d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
|
||||
stroke={fill}
|
||||
fill="none"
|
||||
/>
|
||||
<circle cx={ex} cy={ey} r={2} fill={fill} stroke="none" />
|
||||
<text
|
||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
||||
y={ey}
|
||||
textAnchor={textAnchor}
|
||||
fill="#333"
|
||||
>{`Count: ${value}`}</text>
|
||||
<text
|
||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
||||
y={ey}
|
||||
dy={18}
|
||||
textAnchor={textAnchor}
|
||||
fill="#999"
|
||||
>
|
||||
{`(${(percent * 100).toFixed(2)}%)`}
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,21 +17,21 @@ export default function JobDetailCardsTotalsComponent({ loading, data }) {
|
||||
return (
|
||||
<CardTemplate loading={loading} title={t("jobs.labels.cards.totals")}>
|
||||
{totals ? (
|
||||
<div className='imex-flex-row imex-flex-row__flex-space-around'>
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<Statistic
|
||||
className='imex-flex-row__margin-large'
|
||||
className="imex-flex-row__margin-large"
|
||||
title={t("jobs.labels.total_repairs")}
|
||||
value={Dinero(totals.totals.total_repairs).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
className='imex-flex-row__margin-large'
|
||||
className="imex-flex-row__margin-large"
|
||||
title={t("jobs.fields.ded_amt")}
|
||||
value={Dinero({
|
||||
amount: Math.round((data.ded_amt || 0) * 100),
|
||||
}).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
className='imex-flex-row__margin-large'
|
||||
className="imex-flex-row__margin-large"
|
||||
title={t("jobs.labels.net_repairs")}
|
||||
value={Dinero(totals.totals.net_repairs).toFormat()}
|
||||
/>
|
||||
|
||||
@@ -267,7 +267,6 @@ export function JobLinesComponent({
|
||||
];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
console.log("filters", filters);
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
@@ -286,7 +285,7 @@ export function JobLinesComponent({
|
||||
<Menu.Item key="PAS">{t("joblines.fields.part_types.PAS")}</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
console.log("state", state);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PartsOrderModalContainer />
|
||||
|
||||
@@ -1,51 +1,38 @@
|
||||
import { useQuery } from "@apollo/react-hooks";
|
||||
import React, { useState } from "react";
|
||||
import { GET_JOB_LINES_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import JobLinesComponent from "./job-lines.component";
|
||||
|
||||
function JobLinesContainer({ jobId }) {
|
||||
const { loading, error, data, refetch } = useQuery(GET_JOB_LINES_BY_PK, {
|
||||
variables: { id: jobId },
|
||||
});
|
||||
|
||||
function JobLinesContainer({ jobId, joblines, refetch }) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [selectedLines, setSelectedLines] = useState([]);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
|
||||
const jobLines =
|
||||
data && data.joblines
|
||||
? searchText
|
||||
? data.joblines.filter(
|
||||
(jl) =>
|
||||
(jl.unq_seq || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.line_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.part_type || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.oem_partno || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.op_code_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.db_price || "")
|
||||
.toString()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.act_price || "").toString().includes(searchText.toLowerCase())
|
||||
)
|
||||
: data.joblines
|
||||
: null;
|
||||
const jobLines = joblines
|
||||
? searchText
|
||||
? joblines.filter(
|
||||
(jl) =>
|
||||
(jl.unq_seq || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.line_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.part_type || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.oem_partno || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.op_code_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(jl.db_price || "").toString().includes(searchText.toLowerCase()) ||
|
||||
(jl.act_price || "").toString().includes(searchText.toLowerCase())
|
||||
)
|
||||
: joblines
|
||||
: null;
|
||||
|
||||
return (
|
||||
<JobLinesComponent
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
jobLines={jobLines}
|
||||
setSearchText={setSearchText}
|
||||
|
||||
@@ -67,62 +67,77 @@ export function JobEmployeeAssignments({
|
||||
return (
|
||||
<div>
|
||||
<Popover destroyTooltipOnHide content={popContent} visible={visibility}>
|
||||
<DataLabel label={t("jobs.fields.employee_body")}>
|
||||
{body ? (
|
||||
<div>
|
||||
<span>{`${body.first_name || ""} ${body.last_name || ""}`}</span>
|
||||
<MinusOutlined
|
||||
operation="body"
|
||||
onClick={() => handleRemove("body")}
|
||||
<div style={{ display: "flex" }}>
|
||||
<DataLabel
|
||||
label={t("jobs.fields.employee_body")}
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
>
|
||||
{body ? (
|
||||
<div>
|
||||
<span>{`${body.first_name || ""} ${
|
||||
body.last_name || ""
|
||||
}`}</span>
|
||||
<MinusOutlined
|
||||
operation="body"
|
||||
onClick={() => handleRemove("body")}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PlusCircleFilled
|
||||
onClick={() => {
|
||||
setAssignment({ operation: "body" });
|
||||
setVisibility(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PlusCircleFilled
|
||||
onClick={() => {
|
||||
setAssignment({ operation: "body" });
|
||||
setVisibility(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</DataLabel>
|
||||
<DataLabel label={t("jobs.fields.employee_prep")}>
|
||||
{prep ? (
|
||||
<div>
|
||||
<span>{`${prep.first_name || ""} ${prep.last_name || ""}`}</span>
|
||||
<MinusOutlined
|
||||
operation="prep"
|
||||
onClick={() => handleRemove("prep")}
|
||||
)}
|
||||
</DataLabel>
|
||||
<DataLabel
|
||||
label={t("jobs.fields.employee_prep")}
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
>
|
||||
{prep ? (
|
||||
<div>
|
||||
<span>{`${prep.first_name || ""} ${
|
||||
prep.last_name || ""
|
||||
}`}</span>
|
||||
<MinusOutlined
|
||||
operation="prep"
|
||||
onClick={() => handleRemove("prep")}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PlusCircleFilled
|
||||
onClick={() => {
|
||||
setAssignment({ operation: "prep" });
|
||||
setVisibility(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PlusCircleFilled
|
||||
onClick={() => {
|
||||
setAssignment({ operation: "prep" });
|
||||
setVisibility(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</DataLabel>
|
||||
<DataLabel label={t("jobs.fields.employee_refinish")}>
|
||||
{refinish ? (
|
||||
<div>
|
||||
<span>{`${refinish.first_name || ""} ${
|
||||
refinish.last_name || ""
|
||||
}`}</span>
|
||||
<MinusOutlined
|
||||
operation="refinish"
|
||||
onClick={() => handleRemove("refinish")}
|
||||
)}
|
||||
</DataLabel>
|
||||
<DataLabel
|
||||
label={t("jobs.fields.employee_refinish")}
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
>
|
||||
{refinish ? (
|
||||
<div>
|
||||
<span>{`${refinish.first_name || ""} ${
|
||||
refinish.last_name || ""
|
||||
}`}</span>
|
||||
<MinusOutlined
|
||||
operation="refinish"
|
||||
onClick={() => handleRemove("refinish")}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PlusCircleFilled
|
||||
onClick={() => {
|
||||
setAssignment({ operation: "refinish" });
|
||||
setVisibility(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PlusCircleFilled
|
||||
onClick={() => {
|
||||
setAssignment({ operation: "refinish" });
|
||||
setVisibility(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</DataLabel>
|
||||
)}
|
||||
</DataLabel>
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -30,7 +30,6 @@ export function JobIntakeForm({ formItems, bodyshop }) {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
console.log("values", values);
|
||||
setLoading(true);
|
||||
logImEXEvent("job_complete_intake");
|
||||
|
||||
@@ -71,7 +70,6 @@ export function JobIntakeForm({ formItems, bodyshop }) {
|
||||
}),
|
||||
});
|
||||
}
|
||||
console.log("handleFinish -> result", result);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,11 +16,6 @@ export default function JobReconciliationModalComponent({ job, invoices }) {
|
||||
)
|
||||
.flat() || [];
|
||||
|
||||
console.log(
|
||||
"JobReconciliationModalComponent -> invoiceLineData",
|
||||
invoiceLineData
|
||||
);
|
||||
|
||||
const jobLineData = job.joblines.filter((j) => j.part_type !== null);
|
||||
|
||||
return (
|
||||
|
||||
@@ -36,9 +36,11 @@ const JobSearchSelect = (
|
||||
<Option key={o.id} value={o.id}>
|
||||
{`${o.ro_number ? o.ro_number : o.est_number} | ${
|
||||
o.ownr_ln || ""
|
||||
} ${o.ownr_fn || ""} | ${o.v_model_yr || ""} ${
|
||||
o.v_make_desc || ""
|
||||
} ${o.v_model_desc || ""}`}
|
||||
} ${o.ownr_fn || ""} ${
|
||||
o.ownr_co_nm ? ` ${o.ownr_co_num}` : ""
|
||||
}| ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${
|
||||
o.v_model_desc || ""
|
||||
}`}
|
||||
</Option>
|
||||
))
|
||||
: null}
|
||||
|
||||
@@ -1,219 +1,305 @@
|
||||
import { Statistic } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Col, Result, Row, Statistic, Typography } from "antd";
|
||||
import Dinero from "dinero.js";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import JobCalculateTotals from "../job-calculate-totals/job-calculate-totals.component";
|
||||
import "./job-totals-table.styles.scss";
|
||||
import { CalculateJob } from "./job-totals.utility";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const colSpan = {
|
||||
md: { span: 24 },
|
||||
lg: { span: 12 },
|
||||
};
|
||||
|
||||
export function JobsTotalsTableComponent({ bodyshop, job }) {
|
||||
const { t } = useTranslation();
|
||||
const [totals, setTotals] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
setTotals(CalculateJob(job, bodyshop.shoprates));
|
||||
}, [bodyshop, job]);
|
||||
console.log("job", job);
|
||||
|
||||
if (!!!totals) {
|
||||
return <LoadingSkeleton />;
|
||||
if (!!!job.job_totals) {
|
||||
return (
|
||||
<Result
|
||||
title={t("jobs.errors.nofinancial")}
|
||||
extra={<JobCalculateTotals job={job} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='job-totals-container'>
|
||||
<div className='job-totals-tables'>
|
||||
<table className='job-totals-rates-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t("jobs.labels.rates")}</th>
|
||||
<th>$</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_laa")}</td>
|
||||
<td>{totals.rates.laa.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.laa.hours.toFixed(2)} @ ${
|
||||
totals.rates.laa.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lab")}</td>
|
||||
<td>{totals.rates.lab.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.lab.hours.toFixed(2)} @ ${
|
||||
totals.rates.lab.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lad")}</td>
|
||||
<td>{totals.rates.lad.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.lad.hours.toFixed(2)} @ ${
|
||||
totals.rates.lad.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lae")}</td>
|
||||
<td>{totals.rates.lae.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.lae.hours.toFixed(2)} @ ${
|
||||
totals.rates.lae.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_laf")}</td>
|
||||
<td>{totals.rates.laf.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.laf.hours.toFixed(2)} @ ${
|
||||
totals.rates.laf.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lag")}</td>
|
||||
<td>{totals.rates.lag.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.lag.hours.toFixed(2)} @ ${
|
||||
totals.rates.lag.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lam")}</td>
|
||||
<td>{totals.rates.lam.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.lam.hours.toFixed(2)} @ ${
|
||||
totals.rates.lam.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lar")}</td>
|
||||
<td>{totals.rates.lar.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.lar.hours.toFixed(2)} @ ${
|
||||
totals.rates.lar.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_las")}</td>
|
||||
<td>{totals.rates.las.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.las.hours.toFixed(2)} @ ${
|
||||
totals.rates.las.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lau")}</td>
|
||||
<td>{totals.rates.lau.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.lau.hours.toFixed(2)} @ ${
|
||||
totals.rates.lau.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_la1")}</td>
|
||||
<td>{totals.rates.la1.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.la1.hours.toFixed(2)} @ ${
|
||||
totals.rates.la1.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_la2")}</td>
|
||||
<td>{totals.rates.la2.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.la2.hours.toFixed(2)} @ ${
|
||||
totals.rates.la2.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_la3")}</td>
|
||||
<td>{totals.rates.la3.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.la3.hours.toFixed(2)} @ ${
|
||||
totals.rates.la3.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_la4")}</td>
|
||||
<td>{totals.rates.la4.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.la4.hours.toFixed(2)} @ ${
|
||||
totals.rates.la4.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_atp")}</td>
|
||||
<td>{totals.rates.atp.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.atp.hours.toFixed(2)} @ ${
|
||||
totals.rates.atp.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.labels.mapa")}</td>
|
||||
<td>{totals.rates.mapa.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.mapa.hours.toFixed(2)} @ ${
|
||||
totals.rates.mapa.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.labels.mash")}</td>
|
||||
<td>{totals.rates.mash.total.toFormat()}</td>
|
||||
<td>{`(${totals.rates.mash.hours.toFixed(2)} @ ${
|
||||
totals.rates.mash.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.labels.rates_subtotal")}</td>
|
||||
<td>{totals.rates.subtotal.toFormat()}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table className='job-totals-parts-table'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{t("jobs.labels.partstotal")}</td>
|
||||
<td>{totals.parts.parts.total.toFormat()}</td>
|
||||
<td>{`(${totals.parts.parts.subtotal.toFormat()} ± ${totals.parts.parts.adjustments.toFormat()})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.labels.subletstotal")}</td>
|
||||
<td>{totals.parts.sublets.total.toFormat()}</td>
|
||||
<td>{`(${totals.parts.sublets.subtotal.toFormat()} ± ${totals.parts.sublets.adjustments.toFormat()})`}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div
|
||||
className='job-totals-stats'
|
||||
onClick={(e) => {
|
||||
if (e.detail === 3) {
|
||||
try {
|
||||
console.log("Job", job);
|
||||
} catch {
|
||||
console.log("Unable to show job.");
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<Statistic
|
||||
title={t("jobs.labels.subtotal")}
|
||||
value={totals.totals.subtotal.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.state_tax_amt")}
|
||||
value={totals.totals.state_tax.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.local_tax_amt")}
|
||||
value={totals.totals.local_tax.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.federal_tax_amt")}
|
||||
value={totals.totals.federal_tax.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.total_repairs")}
|
||||
value={totals.totals.total_repairs.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.net_repairs")}
|
||||
value={totals.totals.net_repairs.toFormat()}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Row gutter={[32, 32]}>
|
||||
<Col {...colSpan}>
|
||||
<div className="job-totals-half">
|
||||
<Typography.Title level={4}>
|
||||
{t("jobs.labels.labortotals")}
|
||||
</Typography.Title>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_laa")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.laa.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.laa.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.laa.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lab")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.lab.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.lab.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.lab.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lad")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.lad.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.lad.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.lad.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lae")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.lae.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.lae.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.lae.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_laf")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.laf.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.laf.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.laf.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lag")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.lag.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.lag.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.lag.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lam")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.lam.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.lam.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.lam.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lar")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.lar.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.lar.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.lar.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_las")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.las.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.las.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.las.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_lau")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.lau.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.lau.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.lau.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_la1")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.la1.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.la1.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.la1.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_la2")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.la2.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.la2.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.la2.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_la3")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.la3.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.la3.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.la3.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_la4")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.la4.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.la4.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.la4.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.fields.rate_atp")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.atp.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.atp.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.atp.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{t("jobs.labels.rates_subtotal")}</td>
|
||||
<td>{Dinero(job.job_totals.rates.subtotal).toFormat()}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Col>
|
||||
<Col {...colSpan}>
|
||||
<div className="job-totals-half">
|
||||
<table>
|
||||
<tbody>
|
||||
{Object.keys(job.job_totals.parts.parts.list).map(
|
||||
(key, idx) => (
|
||||
<tr key={idx}>
|
||||
<td>{t(`jobs.fields.${key.toLowerCase()}`)}</td>
|
||||
<td className="currency">
|
||||
{Dinero(
|
||||
job.job_totals.parts.parts.list[key].total
|
||||
).toFormat()}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
)
|
||||
)}
|
||||
<tr>
|
||||
<td>{t("jobs.labels.partstotal")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.parts.parts.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${Dinero(
|
||||
job.job_totals.parts.parts.subtotal
|
||||
).toFormat()} ± ${Dinero(
|
||||
job.job_totals.parts.parts.adjustments
|
||||
).toFormat()})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.labels.subletstotal")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.parts.sublets.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${Dinero(
|
||||
job.job_totals.parts.sublets.subtotal
|
||||
).toFormat()} ± ${Dinero(
|
||||
job.job_totals.parts.sublets.adjustments
|
||||
).toFormat()})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.labels.mapa")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.mapa.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.mapa.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.mapa.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("jobs.labels.mash")}</td>
|
||||
<td className="currency">
|
||||
{Dinero(job.job_totals.rates.mash.total).toFormat()}
|
||||
</td>
|
||||
<td>{`(${job.job_totals.rates.mash.hours.toFixed(2)} @ ${
|
||||
job.job_totals.rates.mash.rate
|
||||
})`}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
className="job-totals-stats"
|
||||
onClick={(e) => {
|
||||
if (e.detail === 3) {
|
||||
try {
|
||||
console.log("Job", job);
|
||||
} catch {
|
||||
console.log("Unable to show job.");
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Statistic
|
||||
title={t("jobs.labels.state_tax_amt")}
|
||||
value={Dinero(job.job_totals.totals.state_tax).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.local_tax_amt")}
|
||||
value={Dinero(job.job_totals.totals.local_tax).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.federal_tax_amt")}
|
||||
value={Dinero(job.job_totals.totals.federal_tax).toFormat()}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="job-totals-stats"
|
||||
onClick={(e) => {
|
||||
if (e.detail === 3) {
|
||||
try {
|
||||
console.log("Job", job);
|
||||
} catch {
|
||||
console.log("Unable to show job.");
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Statistic
|
||||
title={t("jobs.labels.subtotal")}
|
||||
value={Dinero(job.job_totals.totals.subtotal).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.total_repairs")}
|
||||
value={Dinero(job.job_totals.totals.total_repairs).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.net_repairs")}
|
||||
value={Dinero(job.job_totals.totals.net_repairs).toFormat()}
|
||||
/>
|
||||
</div>
|
||||
<JobCalculateTotals job={job} />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,38 @@
|
||||
.job-totals-container {
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
}
|
||||
.job-totals-tables {
|
||||
.job-totals-half {
|
||||
flex: 1;
|
||||
display: block;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.job-totals-rates-table,
|
||||
.job-totals-parts-table {
|
||||
border: black;
|
||||
width: 100%;
|
||||
table {
|
||||
border: 1px solid #ccc;
|
||||
border-collapse: collapse;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 80%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
table tr {
|
||||
//background-color: #f8f8f8;
|
||||
border: 1px solid #ddd;
|
||||
padding: 0.35em;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
padding: 0.625em;
|
||||
//text-align: center;
|
||||
}
|
||||
table td.currency {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.job-totals-stats {
|
||||
margin: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
.ant-statistic {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
width: 100%;
|
||||
//flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
@@ -1,323 +1,322 @@
|
||||
import Dinero from "dinero.js";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
// import Dinero from "dinero.js";
|
||||
// import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
export function CalculateJob(job, shoprates) {
|
||||
logImEXEvent("job_calculate_total");
|
||||
// export function CalculateJob(job, shoprates) {
|
||||
// logImEXEvent("job_calculate_total");
|
||||
|
||||
let ret = {
|
||||
parts: CalculatePartsTotals(job.joblines),
|
||||
rates: CalculateRatesTotals(job, shoprates),
|
||||
custPayable: CalculateCustPayable(job),
|
||||
};
|
||||
ret.totals = CalculateTaxesTotals(job, ret);
|
||||
console.log("CalculateJob -> Final", ret);
|
||||
return ret;
|
||||
}
|
||||
// let ret = {
|
||||
// parts: CalculatePartsTotals(job.joblines),
|
||||
// rates: CalculateRatesTotals(job, shoprates),
|
||||
// custPayable: CalculateCustPayable(job),
|
||||
// };
|
||||
// ret.totals = CalculateTaxesTotals(job, ret);
|
||||
// console.log("CalculateJob -> Final", ret);
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
function CalculateTaxesTotals(job, otherTotals) {
|
||||
const subtotal = otherTotals.parts.parts.subtotal
|
||||
.add(otherTotals.parts.sublets.subtotal)
|
||||
.add(otherTotals.rates.subtotal)
|
||||
.add(Dinero({ amount: (job.towing_payable || 0) * 100 }))
|
||||
.add(Dinero({ amount: (job.storage_payable || 0) * 100 }));
|
||||
//TODO Levies should be included??
|
||||
// function CalculateTaxesTotals(job, otherTotals) {
|
||||
// const subtotal = otherTotals.parts.parts.subtotal
|
||||
// .add(otherTotals.parts.sublets.subtotal)
|
||||
// .add(otherTotals.rates.subtotal)
|
||||
// .add(Dinero({ amount: (job.towing_payable || 0) * 100 }))
|
||||
// .add(Dinero({ amount: (job.storage_payable || 0) * 100 }));
|
||||
// //TODO Levies should be included??
|
||||
|
||||
const statePartsTax = job.joblines.reduce((acc, val) => {
|
||||
if (!!!val.tax_part) return acc;
|
||||
if (!!job.parts_tax_rates[val.part_type]) {
|
||||
return acc.add(
|
||||
Dinero({ amount: Math.round(val.act_price * 100) })
|
||||
.multiply(val.part_qty)
|
||||
.percentage(
|
||||
(job.parts_tax_rates[val.part_type].prt_tax_rt || 0) * 100
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
}, Dinero({ amount: 0 }));
|
||||
console.log("job.federal_tax_rate ", job.federal_tax_rate);
|
||||
console.log(subtotal.percentage((job.federal_tax_rate || 0) * 100));
|
||||
let ret = {
|
||||
subtotal: subtotal,
|
||||
federal_tax: subtotal.percentage((job.federal_tax_rate || 0) * 100),
|
||||
statePartsTax,
|
||||
state_tax: statePartsTax
|
||||
.add(
|
||||
otherTotals.rates.rates_subtotal.percentage((job.tax_lbr_rt || 0) * 100)
|
||||
)
|
||||
.add(
|
||||
Dinero({
|
||||
amount: Math.round((job.towing_payable || 0) * 100),
|
||||
}).percentage((job.tax_tow_rt || 0) * 100)
|
||||
)
|
||||
.add(
|
||||
Dinero({
|
||||
amount: Math.round((job.storage_payable || 0) * 100),
|
||||
}).percentage((job.tax_str_rt || 0) * 100)
|
||||
)
|
||||
.add(
|
||||
otherTotals.rates.mapa.total
|
||||
.add(otherTotals.rates.mash.total)
|
||||
.percentage((job.tax_paint_mat_rt || 0) * 100)
|
||||
),
|
||||
local_tax: subtotal.percentage((job.local_tax_rate || 0) * 100),
|
||||
};
|
||||
ret.total_repairs = ret.subtotal
|
||||
.add(ret.federal_tax)
|
||||
.add(ret.state_tax)
|
||||
.add(ret.local_tax);
|
||||
ret.net_repairs = ret.total_repairs.subtract(otherTotals.custPayable.total);
|
||||
// const statePartsTax = job.joblines.reduce((acc, val) => {
|
||||
// if (!!!val.tax_part) return acc;
|
||||
// if (!!job.parts_tax_rates[val.part_type]) {
|
||||
// return acc.add(
|
||||
// Dinero({ amount: Math.round(val.act_price * 100) })
|
||||
// .multiply(val.part_qty)
|
||||
// .percentage(
|
||||
// (job.parts_tax_rates[val.part_type].prt_tax_rt || 0) * 100
|
||||
// )
|
||||
// );
|
||||
// } else {
|
||||
// return acc;
|
||||
// }
|
||||
// }, Dinero({ amount: 0 }));
|
||||
// let ret = {
|
||||
// subtotal: subtotal,
|
||||
// federal_tax: subtotal.percentage((job.federal_tax_rate || 0) * 100),
|
||||
// statePartsTax,
|
||||
// state_tax: statePartsTax
|
||||
// .add(
|
||||
// otherTotals.rates.rates_subtotal.percentage((job.tax_lbr_rt || 0) * 100)
|
||||
// )
|
||||
// .add(
|
||||
// Dinero({
|
||||
// amount: Math.round((job.towing_payable || 0) * 100),
|
||||
// }).percentage((job.tax_tow_rt || 0) * 100)
|
||||
// )
|
||||
// .add(
|
||||
// Dinero({
|
||||
// amount: Math.round((job.storage_payable || 0) * 100),
|
||||
// }).percentage((job.tax_str_rt || 0) * 100)
|
||||
// )
|
||||
// .add(
|
||||
// otherTotals.rates.mapa.total
|
||||
// .add(otherTotals.rates.mash.total)
|
||||
// .percentage((job.tax_paint_mat_rt || 0) * 100)
|
||||
// ),
|
||||
// local_tax: subtotal.percentage((job.local_tax_rate || 0) * 100),
|
||||
// };
|
||||
// ret.total_repairs = ret.subtotal
|
||||
// .add(ret.federal_tax)
|
||||
// .add(ret.state_tax)
|
||||
// .add(ret.local_tax);
|
||||
// ret.net_repairs = ret.total_repairs.subtract(otherTotals.custPayable.total);
|
||||
|
||||
return ret;
|
||||
}
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
//Rates are multipled by 10 to reduce the errors of rounding.
|
||||
//Adjusted for when adding to total by dividing by 10.
|
||||
function CalculateRatesTotals(ratesList, shoprates) {
|
||||
const jobLines = ratesList.joblines;
|
||||
// //Rates are multipled by 10 to reduce the errors of rounding.
|
||||
// //Adjusted for when adding to total by dividing by 10.
|
||||
// function CalculateRatesTotals(ratesList, shoprates) {
|
||||
// const jobLines = ratesList.joblines;
|
||||
|
||||
let ret = {
|
||||
la1: {
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LA1")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
rate: ratesList.rate_la1 || 0,
|
||||
},
|
||||
la2: {
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LA2")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
rate: ratesList.rate_la2 || 0,
|
||||
},
|
||||
la3: {
|
||||
rate: ratesList.rate_la3 || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LA3")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
la4: {
|
||||
rate: ratesList.rate_la4 || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LA4")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
laa: {
|
||||
rate: ratesList.rate_laa || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAA")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
lab: {
|
||||
rate: ratesList.rate_lab || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAB")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
lad: {
|
||||
rate: ratesList.rate_lad || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAD")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
lae: {
|
||||
rate: ratesList.rate_lae || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAE")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
laf: {
|
||||
rate: ratesList.rate_laf || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAF")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
lag: {
|
||||
rate: ratesList.rate_lag || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAG")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
lam: {
|
||||
rate: ratesList.rate_lam || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAM")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
lar: {
|
||||
rate: ratesList.rate_lar || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAR")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
las: {
|
||||
rate: ratesList.rate_las || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAS")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
lau: {
|
||||
rate: ratesList.rate_lau || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAU")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
atp: {
|
||||
rate: shoprates.rate_atp || 0,
|
||||
hours:
|
||||
jobLines.filter((item) => item.line_desc.includes("ATS Amount"))
|
||||
.length > 0
|
||||
? jobLines
|
||||
.filter(
|
||||
(item) =>
|
||||
item.mod_lbr_ty !== "LA1" &&
|
||||
item.mod_lbr_ty !== "LA2" &&
|
||||
item.mod_lbr_ty !== "LA3" &&
|
||||
item.mod_lbr_ty !== "LA4" &&
|
||||
item.mod_lbr_ty !== "LAU" &&
|
||||
item.mod_lbr_ty !== "LAG" &&
|
||||
item.mod_lbr_ty !== "LAS" &&
|
||||
item.mod_lbr_ty !== "LAA"
|
||||
)
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0)
|
||||
: 0,
|
||||
},
|
||||
mapa: {
|
||||
rate: ratesList.rate_mapa || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty === "LAR")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
mash: {
|
||||
rate: ratesList.rate_mash || 0,
|
||||
hours: jobLines
|
||||
.filter((item) => item.mod_lbr_ty !== "LAR")
|
||||
.reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
},
|
||||
};
|
||||
// let ret = {
|
||||
// la1: {
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LA1")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// rate: ratesList.rate_la1 || 0,
|
||||
// },
|
||||
// la2: {
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LA2")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// rate: ratesList.rate_la2 || 0,
|
||||
// },
|
||||
// la3: {
|
||||
// rate: ratesList.rate_la3 || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LA3")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// la4: {
|
||||
// rate: ratesList.rate_la4 || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LA4")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// laa: {
|
||||
// rate: ratesList.rate_laa || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAA")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// lab: {
|
||||
// rate: ratesList.rate_lab || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAB")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// lad: {
|
||||
// rate: ratesList.rate_lad || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAD")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// lae: {
|
||||
// rate: ratesList.rate_lae || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAE")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// laf: {
|
||||
// rate: ratesList.rate_laf || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAF")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// lag: {
|
||||
// rate: ratesList.rate_lag || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAG")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// lam: {
|
||||
// rate: ratesList.rate_lam || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAM")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// lar: {
|
||||
// rate: ratesList.rate_lar || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAR")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// las: {
|
||||
// rate: ratesList.rate_las || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAS")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// lau: {
|
||||
// rate: ratesList.rate_lau || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAU")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// atp: {
|
||||
// rate: shoprates.rate_atp || 0,
|
||||
// hours:
|
||||
// jobLines.filter((item) => item.line_desc.includes("ATS Amount"))
|
||||
// .length > 0
|
||||
// ? jobLines
|
||||
// .filter(
|
||||
// (item) =>
|
||||
// item.mod_lbr_ty !== "LA1" &&
|
||||
// item.mod_lbr_ty !== "LA2" &&
|
||||
// item.mod_lbr_ty !== "LA3" &&
|
||||
// item.mod_lbr_ty !== "LA4" &&
|
||||
// item.mod_lbr_ty !== "LAU" &&
|
||||
// item.mod_lbr_ty !== "LAG" &&
|
||||
// item.mod_lbr_ty !== "LAS" &&
|
||||
// item.mod_lbr_ty !== "LAA"
|
||||
// )
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0)
|
||||
// : 0,
|
||||
// },
|
||||
// mapa: {
|
||||
// rate: ratesList.rate_mapa || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty === "LAR")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// mash: {
|
||||
// rate: ratesList.rate_mash || 0,
|
||||
// hours: jobLines
|
||||
// .filter((item) => item.mod_lbr_ty !== "LAR")
|
||||
// .reduce((acc, value) => acc + value.mod_lb_hrs * 10, 0),
|
||||
// },
|
||||
// };
|
||||
|
||||
let subtotal = Dinero({ amount: 0 });
|
||||
let rates_subtotal = Dinero({ amount: 0 });
|
||||
for (const property in ret) {
|
||||
ret[property].total = Dinero({ amount: ret[property].rate * 100 })
|
||||
.multiply(ret[property].hours)
|
||||
.divide(10);
|
||||
subtotal = subtotal.add(ret[property].total);
|
||||
if (
|
||||
property !== "mapa" &&
|
||||
property !== "mash"
|
||||
//&& property !== "rate_atp"
|
||||
)
|
||||
rates_subtotal = rates_subtotal.add(ret[property].total);
|
||||
}
|
||||
ret.subtotal = subtotal;
|
||||
ret.rates_subtotal = rates_subtotal;
|
||||
// let subtotal = Dinero({ amount: 0 });
|
||||
// let rates_subtotal = Dinero({ amount: 0 });
|
||||
// for (const property in ret) {
|
||||
// ret[property].total = Dinero({ amount: ret[property].rate * 100 })
|
||||
// .multiply(ret[property].hours)
|
||||
// .divide(10);
|
||||
// subtotal = subtotal.add(ret[property].total);
|
||||
// if (
|
||||
// property !== "mapa" &&
|
||||
// property !== "mash"
|
||||
// //&& property !== "rate_atp"
|
||||
// )
|
||||
// rates_subtotal = rates_subtotal.add(ret[property].total);
|
||||
// }
|
||||
// ret.subtotal = subtotal;
|
||||
// ret.rates_subtotal = rates_subtotal;
|
||||
|
||||
return ret;
|
||||
}
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
function CalculatePartsTotals(jobLines) {
|
||||
const ret = jobLines.reduce(
|
||||
(acc, value) => {
|
||||
switch (value.part_type) {
|
||||
case "PAS":
|
||||
case "PASL":
|
||||
return {
|
||||
...acc,
|
||||
sublets: {
|
||||
...acc.sublets,
|
||||
subtotal: acc.sublets.subtotal.add(
|
||||
Dinero({ amount: Math.round(value.act_price * 100) })
|
||||
),
|
||||
//TODO Add Adjustments in
|
||||
},
|
||||
};
|
||||
// case "PAA":
|
||||
// case "PAC":
|
||||
// case "PAG":
|
||||
// case "PAL":
|
||||
// case "PAM":
|
||||
// case "PAN":
|
||||
// case "PAO":
|
||||
// case "PAP":
|
||||
// case "PAR":
|
||||
default:
|
||||
return {
|
||||
...acc,
|
||||
parts: {
|
||||
...acc.parts,
|
||||
list: {
|
||||
...acc.parts.list,
|
||||
[value.part_type]:
|
||||
acc.parts.list[value.part_type] &&
|
||||
acc.parts.list[value.part_type].total
|
||||
? {
|
||||
total: acc.parts.list[value.part_type].total.add(
|
||||
Dinero({
|
||||
amount: Math.round((value.act_price || 0) * 100),
|
||||
}).multiply(value.part_qty || 1)
|
||||
),
|
||||
}
|
||||
: {
|
||||
total: Dinero({
|
||||
amount: Math.round((value.act_price || 0) * 100),
|
||||
}).multiply(value.part_qty || 1),
|
||||
},
|
||||
},
|
||||
subtotal: acc.parts.subtotal.add(
|
||||
Dinero({ amount: Math.round(value.act_price * 100) }).multiply(
|
||||
value.part_qty
|
||||
)
|
||||
),
|
||||
//TODO Add Adjustments in
|
||||
},
|
||||
};
|
||||
// default:
|
||||
// return acc;
|
||||
}
|
||||
},
|
||||
{
|
||||
parts: {
|
||||
list: {},
|
||||
subtotal: Dinero({ amount: 0 }),
|
||||
adjustments: Dinero({ amount: 0 }),
|
||||
total: Dinero({ amount: 0 }),
|
||||
},
|
||||
sublets: {
|
||||
subtotal: Dinero({ amount: 0 }),
|
||||
adjustments: Dinero({ amount: 0 }),
|
||||
total: Dinero({ amount: 0 }),
|
||||
},
|
||||
}
|
||||
);
|
||||
// function CalculatePartsTotals(jobLines) {
|
||||
// const ret = jobLines.reduce(
|
||||
// (acc, value) => {
|
||||
// switch (value.part_type) {
|
||||
// case "PAS":
|
||||
// case "PASL":
|
||||
// return {
|
||||
// ...acc,
|
||||
// sublets: {
|
||||
// ...acc.sublets,
|
||||
// subtotal: acc.sublets.subtotal.add(
|
||||
// Dinero({ amount: Math.round(value.act_price * 100) })
|
||||
// ),
|
||||
// //TODO Add Adjustments in
|
||||
// },
|
||||
// };
|
||||
// // case "PAA":
|
||||
// // case "PAC":
|
||||
// // case "PAG":
|
||||
// // case "PAL":
|
||||
// // case "PAM":
|
||||
// // case "PAN":
|
||||
// // case "PAO":
|
||||
// // case "PAP":
|
||||
// // case "PAR":
|
||||
// default:
|
||||
// if (value.part_type === null) return acc;
|
||||
// return {
|
||||
// ...acc,
|
||||
// parts: {
|
||||
// ...acc.parts,
|
||||
// list: {
|
||||
// ...acc.parts.list,
|
||||
// [value.part_type]:
|
||||
// acc.parts.list[value.part_type] &&
|
||||
// acc.parts.list[value.part_type].total
|
||||
// ? {
|
||||
// total: acc.parts.list[value.part_type].total.add(
|
||||
// Dinero({
|
||||
// amount: Math.round((value.act_price || 0) * 100),
|
||||
// }).multiply(value.part_qty || 1)
|
||||
// ),
|
||||
// }
|
||||
// : {
|
||||
// total: Dinero({
|
||||
// amount: Math.round((value.act_price || 0) * 100),
|
||||
// }).multiply(value.part_qty || 1),
|
||||
// },
|
||||
// },
|
||||
// subtotal: acc.parts.subtotal.add(
|
||||
// Dinero({ amount: Math.round(value.act_price * 100) }).multiply(
|
||||
// value.part_qty
|
||||
// )
|
||||
// ),
|
||||
// //TODO Add Adjustments in
|
||||
// },
|
||||
// };
|
||||
// // default:
|
||||
// // return acc;
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// parts: {
|
||||
// list: {},
|
||||
// subtotal: Dinero({ amount: 0 }),
|
||||
// adjustments: Dinero({ amount: 0 }),
|
||||
// total: Dinero({ amount: 0 }),
|
||||
// },
|
||||
// sublets: {
|
||||
// subtotal: Dinero({ amount: 0 }),
|
||||
// adjustments: Dinero({ amount: 0 }),
|
||||
// total: Dinero({ amount: 0 }),
|
||||
// },
|
||||
// }
|
||||
// );
|
||||
|
||||
return {
|
||||
parts: {
|
||||
...ret.parts,
|
||||
total: ret.parts.subtotal, //+ ret.parts.adjustments
|
||||
},
|
||||
sublets: {
|
||||
...ret.sublets,
|
||||
total: ret.sublets.subtotal, // + ret.sublets.adjustments,
|
||||
},
|
||||
};
|
||||
}
|
||||
// return {
|
||||
// parts: {
|
||||
// ...ret.parts,
|
||||
// total: ret.parts.subtotal, //+ ret.parts.adjustments
|
||||
// },
|
||||
// sublets: {
|
||||
// ...ret.sublets,
|
||||
// total: ret.sublets.subtotal, // + ret.sublets.adjustments,
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
|
||||
function CalculateCustPayable(job) {
|
||||
let ret = {
|
||||
deductible: Dinero({ amount: (job.ded_amt || 0) * 100 }) || 0,
|
||||
federal_tax: Dinero({ amount: (job.federal_tax_payable || 0) * 100 }), //TODO Should this be renamed to make it more clear this is customer GST?
|
||||
other_customer_amount: Dinero({
|
||||
amount: (job.other_amount_payable || 0) * 100,
|
||||
}),
|
||||
dep_taxes: Dinero({ amount: job.depreciation_taxes || 0 }),
|
||||
};
|
||||
// function CalculateCustPayable(job) {
|
||||
// let ret = {
|
||||
// deductible: Dinero({ amount: (job.ded_amt || 0) * 100 }) || 0,
|
||||
// federal_tax: Dinero({ amount: (job.federal_tax_payable || 0) * 100 }), //TODO Should this be renamed to make it more clear this is customer GST?
|
||||
// other_customer_amount: Dinero({
|
||||
// amount: (job.other_amount_payable || 0) * 100,
|
||||
// }),
|
||||
// dep_taxes: Dinero({ amount: job.depreciation_taxes || 0 }),
|
||||
// };
|
||||
|
||||
ret.total = ret.deductible
|
||||
.add(ret.federal_tax)
|
||||
.add(ret.federal_tax)
|
||||
.add(ret.other_customer_amount)
|
||||
.add(ret.dep_taxes);
|
||||
// ret.total = ret.deductible
|
||||
// .add(ret.federal_tax)
|
||||
// .add(ret.federal_tax)
|
||||
// .add(ret.other_customer_amount)
|
||||
// .add(ret.dep_taxes);
|
||||
|
||||
return ret;
|
||||
}
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Button, notification, Table, Input } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.container";
|
||||
|
||||
@@ -109,7 +109,7 @@ export default function JobsAvailableComponent({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "updated_at" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.updated_at}</DateTimeFormatter>
|
||||
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
|
||||
),
|
||||
//width: "12%",
|
||||
//ellipsis: true
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useMutation, useQuery } from "@apollo/react-hooks";
|
||||
import { notification } from "antd";
|
||||
import Axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { CalculateJob } from "../../components/job-totals-table/job-totals.utility";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
DELETE_ALL_AVAILABLE_NEW_JOBS,
|
||||
QUERY_AVAILABLE_NEW_JOBS,
|
||||
@@ -15,7 +17,6 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import JobsAvailableComponent from "./jobs-available-new.component";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -40,7 +41,7 @@ export function JobsAvailableContainer({
|
||||
const [insertNewJob] = useMutation(INSERT_NEW_JOB);
|
||||
const [loadEstData, estData] = estDataLazyLoad;
|
||||
|
||||
const onModalOk = () => {
|
||||
const onModalOk = async () => {
|
||||
logImEXEvent("job_import_new");
|
||||
|
||||
setModalVisible(false);
|
||||
@@ -59,19 +60,21 @@ export function JobsAvailableContainer({
|
||||
message: t("jobs.errors.creating", { error: "No job data present." }),
|
||||
});
|
||||
} else {
|
||||
const newTotals = CalculateJob(
|
||||
{
|
||||
...estData.data.available_jobs_by_pk.est_data,
|
||||
joblines: estData.data.available_jobs_by_pk.est_data.joblines.data,
|
||||
},
|
||||
bodyshop.shoprates
|
||||
);
|
||||
const newTotals = (
|
||||
await Axios.post("/job/totals", {
|
||||
job: {
|
||||
...estData.data.available_jobs_by_pk.est_data,
|
||||
joblines: estData.data.available_jobs_by_pk.est_data.joblines.data,
|
||||
},
|
||||
shoprates: bodyshop.shoprates,
|
||||
})
|
||||
).data;
|
||||
|
||||
const newJob = {
|
||||
...estData.data.available_jobs_by_pk.est_data,
|
||||
clm_total: newTotals.totals.total_repairs.toFormat("0.00"),
|
||||
owner_owing: newTotals.custPayable.total.toFormat("0.00"),
|
||||
job_totals: JSON.stringify(newTotals),
|
||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||
owner_owing: Dinero(newTotals.custPayable.total).toFormat("0.00"),
|
||||
job_totals: newTotals,
|
||||
};
|
||||
|
||||
insertNewJob({
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Button, notification, Table, Input } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
|
||||
|
||||
@@ -124,7 +124,7 @@ export default function JobsAvailableSupplementComponent({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "updated_at" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.updated_at}</DateTimeFormatter>
|
||||
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
|
||||
),
|
||||
//width: "12%",
|
||||
//ellipsis: true
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { useApolloClient, useMutation, useQuery } from "@apollo/react-hooks";
|
||||
import { notification } from "antd";
|
||||
import Axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
import gql from "graphql-tag";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { CalculateJob } from "../../components/job-totals-table/job-totals.utility";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
DELETE_ALL_AVAILABLE_SUPPLEMENT_JOBS,
|
||||
QUERY_AVAILABLE_SUPPLEMENT_JOBS,
|
||||
@@ -18,7 +20,6 @@ import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import JobsAvailableSupplementComponent from "./jobs-available-supplement.component";
|
||||
import { GetSupplementDelta } from "./jobs-available-supplement.estlines.util";
|
||||
import HeaderFields from "./jobs-available-supplement.headerfields";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -67,7 +68,6 @@ export function JobsAvailableSupplementContainer({
|
||||
} else {
|
||||
//create upsert job
|
||||
let supp = estData.data.available_jobs_by_pk.est_data;
|
||||
console.log("supp before", supp);
|
||||
|
||||
delete supp.owner;
|
||||
delete supp.vehicle;
|
||||
@@ -75,13 +75,15 @@ export function JobsAvailableSupplementContainer({
|
||||
HeaderFields.forEach((item) => delete supp[item]);
|
||||
}
|
||||
|
||||
const newTotals = CalculateJob(
|
||||
{
|
||||
...estData.data.available_jobs_by_pk.est_data,
|
||||
joblines: estData.data.available_jobs_by_pk.est_data.joblines.data,
|
||||
},
|
||||
bodyshop.shoprates
|
||||
);
|
||||
const newTotals = (
|
||||
await Axios.post("/job/totals", {
|
||||
job: {
|
||||
...estData.data.available_jobs_by_pk.est_data,
|
||||
joblines: estData.data.available_jobs_by_pk.est_data.joblines.data,
|
||||
},
|
||||
shoprates: bodyshop.shoprates,
|
||||
})
|
||||
).data;
|
||||
|
||||
let suppDelta = await GetSupplementDelta(
|
||||
client,
|
||||
@@ -100,9 +102,9 @@ export function JobsAvailableSupplementContainer({
|
||||
jobId: selectedJob,
|
||||
job: {
|
||||
...supp,
|
||||
clm_total: newTotals.totals.total_repairs.toFormat("0.00"),
|
||||
owner_owing: newTotals.custPayable.total.toFormat("0.00"),
|
||||
job_totals: JSON.stringify(newTotals),
|
||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||
owner_owing: Dinero(newTotals.custPayable.total).toFormat("0.00"),
|
||||
job_totals: newTotals,
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -140,11 +142,12 @@ export function JobsAvailableSupplementContainer({
|
||||
setSelectedJob(null);
|
||||
};
|
||||
|
||||
if (error) return <AlertComponent type='error' message={error.message} />;
|
||||
if (error) return <AlertComponent type="error" message={error.message} />;
|
||||
return (
|
||||
<LoadingSpinner
|
||||
loading={insertLoading}
|
||||
message={t("jobs.labels.creating_new_job")}>
|
||||
message={t("jobs.labels.creating_new_job")}
|
||||
>
|
||||
<JobsAvailableSupplementComponent
|
||||
loading={loading}
|
||||
data={data}
|
||||
|
||||
@@ -16,7 +16,7 @@ export function JobsCloseSaveButton({
|
||||
bodyshop,
|
||||
suspenseAmount,
|
||||
jobId,
|
||||
jobTotals,
|
||||
|
||||
labMatAllocations,
|
||||
partsAllocations,
|
||||
setInvoicedState,
|
||||
@@ -60,9 +60,10 @@ export function JobsCloseSaveButton({
|
||||
return (
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
type='primary'
|
||||
type="primary"
|
||||
disabled={suspenseAmount > 0 || disabled}
|
||||
loading={loading}>
|
||||
loading={loading}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Descriptions, Statistic } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Dinero from "dinero.js";
|
||||
|
||||
export default function JobsCloseTotals({
|
||||
jobTotals,
|
||||
labMatTotal,
|
||||
@@ -17,47 +19,57 @@ export default function JobsCloseTotals({
|
||||
>
|
||||
<Descriptions.Item label={t("jobs.labels.partstotal")}>
|
||||
<Statistic
|
||||
value={jobTotals.parts.parts.total.toFormat()}
|
||||
suffix={`(${jobTotals.parts.parts.subtotal.toFormat()} ± ${jobTotals.parts.parts.adjustments.toFormat()})`}
|
||||
value={Dinero(jobTotals.parts.parts.total).toFormat()}
|
||||
suffix={`(${Dinero(
|
||||
jobTotals.parts.parts.subtotal
|
||||
).toFormat()} ± ${Dinero(
|
||||
jobTotals.parts.parts.adjustments
|
||||
).toFormat()})`}
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.labels.subletstotal")}>
|
||||
<Statistic
|
||||
value={jobTotals.parts.sublets.total.toFormat()}
|
||||
suffix={`(${jobTotals.parts.sublets.subtotal.toFormat()} ± ${jobTotals.parts.sublets.adjustments.toFormat()})`}
|
||||
value={Dinero(jobTotals.parts.sublets.total).toFormat()}
|
||||
suffix={`(${Dinero(
|
||||
jobTotals.parts.sublets.subtotal
|
||||
).toFormat()} ± ${Dinero(
|
||||
jobTotals.parts.sublets.adjustments
|
||||
).toFormat()})`}
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.labels.subtotal")}>
|
||||
<Statistic value={jobTotals.totals.subtotal.toFormat()} />
|
||||
<Statistic value={Dinero(jobTotals.totals.subtotal).toFormat()} />
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.labels.federal_tax_amt")}>
|
||||
<Statistic value={jobTotals.totals.federal_tax.toFormat()} />
|
||||
<Statistic value={Dinero(jobTotals.totals.federal_tax).toFormat()} />
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.labels.state_tax_amt")}>
|
||||
<Statistic value={jobTotals.totals.state_tax.toFormat()} />
|
||||
<Statistic value={Dinero(jobTotals.totals.state_tax).toFormat()} />
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.labels.local_tax_amt")}>
|
||||
<Statistic value={jobTotals.totals.local_tax.toFormat()} />
|
||||
<Statistic value={Dinero(jobTotals.totals.local_tax).toFormat()} />
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Statistic
|
||||
title={t("jobs.labels.total_repairs")}
|
||||
value={jobTotals.totals.total_repairs.toFormat()}
|
||||
value={Dinero(jobTotals.totals.total_repairs).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.net_repairs")}
|
||||
value={jobTotals.totals.net_repairs.toFormat()}
|
||||
value={Dinero(jobTotals.totals.net_repairs).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("jobs.labels.suspense")}
|
||||
valueStyle={{
|
||||
color:
|
||||
jobTotals.totals.subtotal.subtract(labMatTotal).subtract(partsTotal)
|
||||
Dinero(jobTotals.totals.subtotal)
|
||||
.subtract(labMatTotal)
|
||||
.subtract(partsTotal)
|
||||
.getAmount() === 0
|
||||
? "green"
|
||||
: "red",
|
||||
}}
|
||||
value={jobTotals.totals.subtotal
|
||||
value={Dinero(jobTotals.totals.subtotal)
|
||||
.subtract(labMatTotal)
|
||||
.subtract(partsTotal)
|
||||
.toFormat()}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { Button, notification, Popover, Form, Select } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useMutation } from "react-apollo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { CONVERT_JOB_TO_RO } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function JobsConvertButton({ bodyshop, job, refetch }) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [mutationConvertJob] = useMutation(CONVERT_JOB_TO_RO);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleConvert = (values) => {
|
||||
mutationConvertJob({
|
||||
variables: { jobId: job.id, ...values },
|
||||
}).then((r) => {
|
||||
refetch();
|
||||
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.converted"),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const popMenu = (
|
||||
<div>
|
||||
<Form layout="vertical" onFinish={handleConvert}>
|
||||
<Form.Item
|
||||
name={["ins_co_nm"]}
|
||||
label={t("jobs.fields.ins_co_nm")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{bodyshop.md_ins_cos.map((s) => (
|
||||
<Select.Option key={s} value={s}>
|
||||
{s}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["class"]}
|
||||
label={t("jobs.fields.class")}
|
||||
rules={[
|
||||
{
|
||||
required: bodyshop.enforce_class,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{bodyshop.md_classes.map((s) => (
|
||||
<Select.Option key={s} value={s}>
|
||||
{s}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Button type="danger" htmlType="submit">
|
||||
{t("jobs.actions.convert")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisible(false)}>
|
||||
{t("general.actions.close")}
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover visible={visible} content={popMenu}>
|
||||
<Button
|
||||
key="convert"
|
||||
className="imex-flex-row__margin"
|
||||
type="danger"
|
||||
style={{ display: job.converted ? "none" : "" }}
|
||||
disabled={job.converted}
|
||||
onClick={() => setVisible(true)}
|
||||
>
|
||||
{t("jobs.actions.convert")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsConvertButton);
|
||||
@@ -1,146 +1,191 @@
|
||||
import { Collapse, Form, Input, InputNumber, Switch } from "antd";
|
||||
import { Collapse, Form, Input, InputNumber, Select, Switch } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import FormDatePicker from '../form-date-picker/form-date-picker.component';
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import FormItemPhone from "../form-items-formatted/phone-form-item.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
export default function JobsCreateJobsInfo({ form }) {
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
const { t } = useTranslation();
|
||||
const { getFieldValue } = form;
|
||||
return (
|
||||
<div>
|
||||
<Collapse defaultActiveKey='insurance'>
|
||||
<Collapse defaultActiveKey="insurance">
|
||||
<Collapse.Panel
|
||||
key='insurance'
|
||||
header={t("menus.jobsdetail.insurance")}>
|
||||
key="insurance"
|
||||
header={t("menus.jobsdetail.insurance")}
|
||||
>
|
||||
<LayoutFormRow>
|
||||
<Form.Item label={t("jobs.fields.ins_co_id")} name='ins_co_id'>
|
||||
<Form.Item label={t("jobs.fields.ins_co_id")} name="ins_co_id">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.policy_no")} name='policy_no'>
|
||||
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.clm_no")} name='clm_no'>
|
||||
<Form.Item label={t("jobs.fields.clm_no")} name="clm_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.regie_number")}
|
||||
name='regie_number'>
|
||||
name="regie_number"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_date")} name='loss_date'>
|
||||
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_co_nm")} name='ins_co_nm'>
|
||||
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_addr1")} name='ins_addr1'>
|
||||
<Form.Item label={t("jobs.fields.ins_addr1")} name="ins_addr1">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_city")} name='ins_city'>
|
||||
<Form.Item label={t("jobs.fields.ins_city")} name="ins_city">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_ct_ln")} name='ins_ct_ln'>
|
||||
<Form.Item label={t("jobs.fields.ins_ct_ln")} name="ins_ct_ln">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_ct_fn")} name='ins_ct_fn'>
|
||||
<Form.Item label={t("jobs.fields.ins_ct_fn")} name="ins_ct_fn">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_ph1")} name='ins_ph1'>
|
||||
<Form.Item label={t("jobs.fields.ins_ph1")} name="ins_ph1">
|
||||
<FormItemPhone customInput={Input} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.ins_ea")}
|
||||
name='ins_ea'
|
||||
name="ins_ea"
|
||||
rules={[
|
||||
{
|
||||
type: "email",
|
||||
message: "This is not a valid email address.",
|
||||
},
|
||||
]}>
|
||||
]}
|
||||
>
|
||||
<FormItemEmail email={getFieldValue("ins_ea")} />
|
||||
</Form.Item>
|
||||
Appraiser Info
|
||||
<Form.Item label={t("jobs.fields.est_co_nm")} name='est_co_nm'>
|
||||
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.est_ct_fn")} name='est_ct_fn'>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.est_ct_fn")}
|
||||
name="est_ct_fn"
|
||||
rules={[
|
||||
{
|
||||
required: selected && true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.est_ct_ln")} name='est_ct_ln'>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.est_ct_ln")}
|
||||
name="est_ct_ln"
|
||||
rules={[
|
||||
{
|
||||
required: selected && true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.pay_date")} name='pay_date'>
|
||||
<Form.Item label={t("jobs.fields.pay_date")} name="pay_date">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.est_ph1")} name='est_ph1'>
|
||||
<Form.Item label={t("jobs.fields.est_ph1")} name="est_ph1">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.est_ea")}
|
||||
name='est_ea'
|
||||
name="est_ea"
|
||||
rules={[
|
||||
{
|
||||
type: "email",
|
||||
message: "This is not a valid email address.",
|
||||
},
|
||||
]}>
|
||||
]}
|
||||
>
|
||||
<FormItemEmail email={getFieldValue("est_ea")} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.selling_dealer")}
|
||||
name='selling_dealer'>
|
||||
name="selling_dealer"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.servicing_dealer")}
|
||||
name='servicing_dealer'>
|
||||
name="servicing_dealer"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.selling_dealer_contact")}
|
||||
name='selling_dealer_contact'>
|
||||
name="selling_dealer_contact"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.servicing_dealer_contact")}
|
||||
name='servicing_dealer_contact'>
|
||||
name="servicing_dealer_contact"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel key='claim' header={t("menus.jobsdetail.claimdetail")}>
|
||||
<Collapse.Panel key="claim" header={t("menus.jobsdetail.claimdetail")}>
|
||||
<LayoutFormRow>
|
||||
<Form.Item label={t("jobs.fields.csr")} name='csr'>
|
||||
<Form.Item label={t("jobs.fields.csr")} name="csr">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_desc")} name='loss_desc'>
|
||||
<Form.Item label={t("jobs.fields.loss_desc")} name="loss_desc">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ponumber")} name='po_number'>
|
||||
<Form.Item label={t("jobs.fields.ponumber")} name="po_number">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.unitnumber")} name='unit_number'>
|
||||
<Form.Item label={t("jobs.fields.unitnumber")} name="unit_number">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.specialcoveragepolicy")}
|
||||
valuePropName='checked'
|
||||
name='special_coverage_policy'>
|
||||
valuePropName="checked"
|
||||
name="special_coverage_policy"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.kmin")} name='kmin'>
|
||||
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.kmout")} name='kmout'>
|
||||
<Form.Item label={t("jobs.fields.kmout")} name="kmout">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.referralsource")}
|
||||
name='referral_source'>
|
||||
<Input />
|
||||
name="referral_source"
|
||||
>
|
||||
<Select>
|
||||
{bodyshop.md_referral_sources.map((s) => (
|
||||
<Select.Option key={s} value={s}>
|
||||
{s}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
TODO How to handle different taxes and marking them as exempt?
|
||||
@@ -153,120 +198,128 @@ export default function JobsCreateJobsInfo({ form }) {
|
||||
}
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel
|
||||
key='financial'
|
||||
header={t("menus.jobsdetail.financials")}>
|
||||
key="financial"
|
||||
header={t("menus.jobsdetail.financials")}
|
||||
>
|
||||
<LayoutFormRow>
|
||||
<Form.Item label={t("jobs.fields.ded_amt")} name='ded_amt'>
|
||||
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ded_status")} name='ded_status'>
|
||||
<Form.Item label={t("jobs.fields.ded_status")} name="ded_status">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.depreciation_taxes")}
|
||||
name='depreciation_taxes'>
|
||||
name="depreciation_taxes"
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
TODO This is equivalent of GST payable.
|
||||
<Form.Item
|
||||
label={t("jobs.fields.federal_tax_payable")}
|
||||
name='federal_tax_payable'>
|
||||
name="federal_tax_payable"
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.other_amount_payable")}
|
||||
name='other_amount_payable'>
|
||||
name="other_amount_payable"
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.towing_payable")}
|
||||
name='towing_payable'>
|
||||
name="towing_payable"
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.storage_payable")}
|
||||
name='storage_payable'>
|
||||
name="storage_payable"
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.adjustment_bottom_line")}
|
||||
name='adjustment_bottom_line'>
|
||||
name="adjustment_bottom_line"
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
Totals Table
|
||||
<Form.Item
|
||||
label={t("jobs.fields.labor_rate_desc")}
|
||||
name='labor_rate_desc'>
|
||||
name="labor_rate_desc"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lab")} name='rate_lab'>
|
||||
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lad")} name='rate_lad'>
|
||||
<Form.Item label={t("jobs.fields.rate_lad")} name="rate_lad">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lae")} name='rate_lae'>
|
||||
<Form.Item label={t("jobs.fields.rate_lae")} name="rate_lae">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lar")} name='rate_lar'>
|
||||
<Form.Item label={t("jobs.fields.rate_lar")} name="rate_lar">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_las")} name='rate_las'>
|
||||
<Form.Item label={t("jobs.fields.rate_las")} name="rate_las">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_laf")} name='rate_laf'>
|
||||
<Form.Item label={t("jobs.fields.rate_laf")} name="rate_laf">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lam")} name='rate_lam'>
|
||||
<Form.Item label={t("jobs.fields.rate_lam")} name="rate_lam">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lag")} name='rate_lag'>
|
||||
<Form.Item label={t("jobs.fields.rate_lag")} name="rate_lag">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
Note //TODO Remove ATP rate?
|
||||
<Form.Item label={t("jobs.fields.rate_atp")} name='rate_atp'>
|
||||
<Form.Item label={t("jobs.fields.rate_atp")} name="rate_atp">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lau")} name='rate_lau'>
|
||||
<Form.Item label={t("jobs.fields.rate_lau")} name="rate_lau">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la1")} name='rate_la1'>
|
||||
<Form.Item label={t("jobs.fields.rate_la1")} name="rate_la1">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la2")} name='rate_la2'>
|
||||
<Form.Item label={t("jobs.fields.rate_la2")} name="rate_la2">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la3")} name='rate_la3'>
|
||||
<Form.Item label={t("jobs.fields.rate_la3")} name="rate_la3">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la4")} name='rate_la4'>
|
||||
<Form.Item label={t("jobs.fields.rate_la4")} name="rate_la4">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mapa")} name='rate_mapa'>
|
||||
<Form.Item label={t("jobs.fields.rate_mapa")} name="rate_mapa">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mash")} name='rate_mash'>
|
||||
<Form.Item label={t("jobs.fields.rate_mash")} name="rate_mash">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mahw")} name='rate_mahw'>
|
||||
<Form.Item label={t("jobs.fields.rate_mahw")} name="rate_mahw">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_ma2s")} name='rate_ma2s'>
|
||||
<Form.Item label={t("jobs.fields.rate_ma2s")} name="rate_ma2s">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_ma3s")} name='rate_ma3s'>
|
||||
<Form.Item label={t("jobs.fields.rate_ma3s")} name="rate_ma3s">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mabl")} name='rate_mabl'>
|
||||
<Form.Item label={t("jobs.fields.rate_mabl")} name="rate_mabl">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_macs")} name='rate_macs'>
|
||||
<Form.Item label={t("jobs.fields.rate_macs")} name="rate_macs">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_matd")} name='rate_matd'>
|
||||
<Form.Item label={t("jobs.fields.rate_matd")} name="rate_matd">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_laa")} name='rate_laa'>
|
||||
<Form.Item label={t("jobs.fields.rate_laa")} name="rate_laa">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
@@ -275,3 +328,4 @@ export default function JobsCreateJobsInfo({ form }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsCreateJobsInfo);
|
||||
|
||||
@@ -12,11 +12,11 @@ export default function JobsCreateOwnerContainer() {
|
||||
skip: !state.owner.search,
|
||||
});
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<JobsCreateOwnerInfoComponent
|
||||
loading={loading}
|
||||
owners={data ? data.search_owner : null}
|
||||
owners={data ? data.search_owners : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,19 +24,34 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
||||
selectedid: null,
|
||||
},
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t("jobs.labels.create.newowner")}
|
||||
</Checkbox>
|
||||
|
||||
<LayoutFormRow header={t("owners.forms.name")} grow>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_ln")}
|
||||
name={["owner", "data", "ownr_ln"]}>
|
||||
name={["owner", "data", "ownr_ln"]}
|
||||
rules={[
|
||||
{
|
||||
required: state.owner.new,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_fn")}
|
||||
name={["owner", "data", "ownr_fn"]}>
|
||||
name={["owner", "data", "ownr_fn"]}
|
||||
rules={[
|
||||
{
|
||||
required: state.owner.new,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
@@ -44,12 +59,14 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_title")}
|
||||
name={["owner", "data", "ownr_title"]}>
|
||||
name={["owner", "data", "ownr_title"]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_co_nm")}
|
||||
name={["owner", "data", "ownr_co_nm"]}>
|
||||
name={["owner", "data", "ownr_co_nm"]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
@@ -57,12 +74,14 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
||||
<LayoutFormRow header={t("owners.forms.address")} grow>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_addr1")}
|
||||
name={["owner", "data", "ownr_addr1"]}>
|
||||
name={["owner", "data", "ownr_addr1"]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_addr2")}
|
||||
name={["owner", "data", "ownr_addr2"]}>
|
||||
name={["owner", "data", "ownr_addr2"]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
@@ -70,24 +89,28 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_city")}
|
||||
name={["owner", "data", "ownr_city"]}>
|
||||
name={["owner", "data", "ownr_city"]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_st")}
|
||||
name={["owner", "data", "ownr_st"]}>
|
||||
name={["owner", "data", "ownr_st"]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_zip")}
|
||||
name={["owner", "data", "ownr_zip"]}>
|
||||
name={["owner", "data", "ownr_zip"]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_ctry")}
|
||||
name={["owner", "data", "ownr_ctry"]}>
|
||||
name={["owner", "data", "ownr_ctry"]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
@@ -102,32 +125,31 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
||||
},
|
||||
]}
|
||||
name={["owner", "data", "ownr_ea"]}
|
||||
shouldUpdate>
|
||||
{() => {
|
||||
return (
|
||||
<FormItemEmail
|
||||
//email={form.getFieldValue("ownr_ea")}
|
||||
disabled={!state.owner.new}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
>
|
||||
<FormItemEmail
|
||||
//email={form.getFieldValue("ownr_ea")}
|
||||
disabled={!state.owner.new}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_ph1")}
|
||||
name={["owner", "data", "ownr_ph1"]}>
|
||||
name={["owner", "data", "ownr_ph1"]}
|
||||
>
|
||||
<FormItemPhone customInput={Input} disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
label={t("owners.fields.preferred_contact")}
|
||||
name={["owner", "data", "preferred_contact"]}>
|
||||
name={["owner", "data", "preferred_contact"]}
|
||||
>
|
||||
<Input disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.allow_text_message")}
|
||||
valuePropName='checked'
|
||||
name={["owner", "data", "allow_text_message"]}>
|
||||
valuePropName="checked"
|
||||
name={["owner", "data", "allow_text_message"]}
|
||||
>
|
||||
<Switch disabled={!state.owner.new} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
@@ -36,6 +36,15 @@ export default function JobsCreateOwnerInfoSearchComponent({
|
||||
tableState.sortedInfo.columnKey === "ownr_fn" &&
|
||||
tableState.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("owners.fields.ownr_co_nm"),
|
||||
dataIndex: "ownr_co_nm",
|
||||
key: "ownr_co_nm",
|
||||
sorter: (a, b) => alphaSort(a.ownr_co_nm, b.ownr_co_nm),
|
||||
sortOrder:
|
||||
tableState.sortedInfo.columnKey === "ownr_co_nm" &&
|
||||
tableState.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("owners.fields.ownr_addr1"),
|
||||
dataIndex: "ownr_addr1",
|
||||
@@ -86,9 +95,9 @@ export default function JobsCreateOwnerInfoSearchComponent({
|
||||
loading={loading}
|
||||
title={() => {
|
||||
return (
|
||||
<div className='imex-table-header'>
|
||||
<div className="imex-table-header">
|
||||
<Input.Search
|
||||
className='imex-table-header__search'
|
||||
className="imex-table-header__search"
|
||||
placeholder={t("general.labels.search")}
|
||||
onSearch={(value) => {
|
||||
setState({
|
||||
@@ -101,11 +110,11 @@ export default function JobsCreateOwnerInfoSearchComponent({
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
size='small'
|
||||
size="small"
|
||||
scroll={{ x: true }}
|
||||
pagination={{ position: "top" }}
|
||||
columns={columns}
|
||||
rowKey='id'
|
||||
rowKey="id"
|
||||
dataSource={owners}
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import { Form, Input, InputNumber, Switch, Select } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function JobsDetailClaims({ bodyshop, job }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormRow>
|
||||
<Form.Item label={t("jobs.fields.csr")} name="csr">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ponumber")} name="po_number">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.unitnumber")} name="unit_number">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
|
||||
<InputNumber precision={1} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.kmout")} name="kmout">
|
||||
<InputNumber precision={1} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.exempt")}
|
||||
valuePropName="checked"
|
||||
name="exempt"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.specialcoveragepolicy")}
|
||||
valuePropName="checked"
|
||||
name="special_coverage_policy"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
|
||||
<Form.Item label={t("jobs.fields.referralsource")} name="referral_source">
|
||||
<Select>
|
||||
{bodyshop.md_referral_sources.map((s) => (
|
||||
<Select.Option key={s} value={s}>
|
||||
{s}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailClaims);
|
||||
@@ -1,215 +0,0 @@
|
||||
import { Col, Divider, Form, Row, Select } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import JobTotalsTable from "../job-totals-table/job-totals-table.component";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const stripeTestEnv = process.env.REACT_APP_STRIPE_PUBLIC_KEY; //.includes("test");
|
||||
|
||||
export function JobsDetailFinancials({ job, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const colSpan = {
|
||||
sm: { span: 24 },
|
||||
lg: { span: 12 },
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<FormRow header={t("payments.labels.title")}>
|
||||
<table style={{ width: "100%" }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t("payments.fields.created_at")}</th>
|
||||
<th>{t("payments.fields.payer")}</th>
|
||||
<th>{t("payments.fields.amount")}</th>
|
||||
<th>{t("payments.fields.memo")}</th>
|
||||
<th>{t("payments.fields.type")}</th>
|
||||
<th>{t("payments.fields.transactionid")}</th>
|
||||
<th>{t("payments.fields.stripeid")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{job.payments.map((p, idx) => (
|
||||
<tr key={idx}>
|
||||
<td>
|
||||
<DateTimeFormatter>{p.created_at}</DateTimeFormatter>
|
||||
</td>
|
||||
<td>{p.payer}</td>
|
||||
<td>
|
||||
<CurrencyFormatter>{p.amount}</CurrencyFormatter>
|
||||
</td>
|
||||
<td>{p.memo}</td>
|
||||
<td>{p.type}</td>
|
||||
<td>{p.transactionid}</td>
|
||||
<td>
|
||||
{p.stripeid ? (
|
||||
<a
|
||||
href={
|
||||
stripeTestEnv
|
||||
? `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/test/payments/${p.stripeid}`
|
||||
: `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/payments/${p.stripeid}`
|
||||
}
|
||||
>
|
||||
{p.stripeid}
|
||||
</a>
|
||||
) : null}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</FormRow>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider />
|
||||
<Row gutter={[32, 32]}>
|
||||
<Col {...colSpan}>
|
||||
<FormRow header={t("jobs.forms.dedinfo")}>
|
||||
<Form.Item label={t("jobs.fields.ded_status")} name="ded_status">
|
||||
<Select>
|
||||
<Select.Option value="W">
|
||||
{t("jobs.labels.deductible.waived")}
|
||||
</Select.Option>
|
||||
<Select.Option value="Y">
|
||||
{t("jobs.labels.deductible.yes")}
|
||||
</Select.Option>
|
||||
<Select.Option value="N">
|
||||
{t("jobs.labels.deductible.no")}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.depreciation_taxes")}
|
||||
name="depreciation_taxes"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.other_amount_payable")}
|
||||
name="other_amount_payable"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.towing_payable")}
|
||||
name="towing_payable"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.storage_payable")}
|
||||
name="storage_payable"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.federal_tax_payable")}
|
||||
name="federal_tax_payable"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.adjustment_bottom_line")}
|
||||
name="adjustment_bottom_line"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
<FormRow header={t("jobs.forms.laborrates")}>
|
||||
<Form.Item label={t("jobs.fields.rate_laa")} name="rate_laa">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lad")} name="rate_lad">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lae")} name="rate_lae">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lar")} name="rate_lar">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_las")} name="rate_las">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_laf")} name="rate_laf">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lam")} name="rate_lam">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lag")} name="rate_lag">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la1")} name="rate_la1">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la2")} name="rate_la2">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la3")} name="rate_la3">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la4")} name="rate_la4">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_atp")} name="rate_atp">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lau")} name="rate_lau">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mapa")} name="rate_mapa">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mash")} name="rate_mash">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mahw")} name="rate_mahw">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_ma2s")} name="rate_ma2s">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_ma3s")} name="rate_ma3s">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mabl")} name="rate_mabl">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_macs")} name="rate_macs">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_matd")} name="rate_matd">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
</Col>
|
||||
<Col {...colSpan}>
|
||||
<JobTotalsTable job={job} />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(JobsDetailFinancials);
|
||||
@@ -1,19 +1,58 @@
|
||||
import { Form, Input } from "antd";
|
||||
import { Col, Form, Input, InputNumber, Row, Select, Switch } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import FormItemPhone from "../form-items-formatted/phone-form-item.component";
|
||||
import Car from "../job-damage-visual/job-damage-visual.component";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
export default function JobsDetailInsurance({ job, form }) {
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
const lossColFields = { sm: { span: 24 }, md: { span: 18 }, lg: { span: 20 } };
|
||||
const lossColDamage = { sm: { span: 24 }, md: { span: 6 }, lg: { span: 4 } };
|
||||
export function JobsDetailGeneral({ bodyshop, job, form }) {
|
||||
const { getFieldValue } = form;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormRow header={t("jobs.forms.inscoinfo")}>
|
||||
<FormRow header={t("jobs.forms.claiminfo")}>
|
||||
<Form.Item label={t("jobs.fields.clm_no")} name="clm_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ded_status")} name="ded_status">
|
||||
<Select>
|
||||
<Select.Option value="W">
|
||||
{t("jobs.labels.deductible.waived")}
|
||||
</Select.Option>
|
||||
<Select.Option value="Y">
|
||||
{t("jobs.labels.deductible.yes")}
|
||||
</Select.Option>
|
||||
<Select.Option value="N">
|
||||
{t("jobs.labels.deductible.no")}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.regie_number")} name="regie_number">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_co_id")} name="ins_co_id">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
@@ -47,36 +86,67 @@ export default function JobsDetailInsurance({ job, form }) {
|
||||
>
|
||||
<FormItemEmail email={getFieldValue("ins_ea")} />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
<FormRow header={t("jobs.forms.claiminfo")}>
|
||||
<Form.Item label={t("jobs.fields.clm_no")} name="clm_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.regie_number")} name="regie_number">
|
||||
<Input />
|
||||
<Form.Item
|
||||
label={t("jobs.fields.referralsource")}
|
||||
name="referral_source"
|
||||
>
|
||||
<Select>
|
||||
{bodyshop.md_referral_sources.map((s) => (
|
||||
<Select.Option key={s} value={s}>
|
||||
{s}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
<FormRow header={t("jobs.forms.lossinfo")}>
|
||||
<div style={{ display: "inline", height: "8rem" }}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col {...lossColFields}>
|
||||
<FormRow header={t("jobs.forms.lossinfo")}>
|
||||
<Form.Item label={t("jobs.fields.loss_desc")} name="loss_desc">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
|
||||
<InputNumber precision={1} min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.kmout")} name="kmout">
|
||||
<InputNumber precision={1} min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ponumber")} name="po_number">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.unitnumber")} name="unit_number">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.specialcoveragepolicy")}
|
||||
valuePropName="checked"
|
||||
name="special_coverage_policy"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.tax_registration_number")}
|
||||
name="tax_registration_number"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
</Col>
|
||||
<Col {...lossColDamage}>
|
||||
{job.area_of_damage ? (
|
||||
<Car
|
||||
dmg1={job.area_of_damage.impact1 || null}
|
||||
dmg2={job.area_of_damage.impact2 || null}
|
||||
dmg1={job.area_of_damage.impact1}
|
||||
dmg2={job.area_of_damage.impact2}
|
||||
/>
|
||||
) : (
|
||||
t("jobs.errors.nodamage")
|
||||
)}
|
||||
</div>
|
||||
<Form.Item label={t("jobs.fields.loss_desc")} name="loss_desc">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FormRow header={t("jobs.forms.appraiserinfo")}>
|
||||
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
|
||||
<Input />
|
||||
@@ -103,10 +173,21 @@ export default function JobsDetailInsurance({ job, form }) {
|
||||
<FormItemEmail email={getFieldValue("est_ea")} />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
<FormRow header="TODO: TO BE PLACED">
|
||||
<Form.Item label={t("jobs.fields.pay_date")} name="pay_date">
|
||||
<FormRow header={t("jobs.forms.other")}>
|
||||
<Form.Item label={t("jobs.fields.csr")} name="csr">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("jobs.fields.category")} name="category">
|
||||
<Select>
|
||||
{bodyshop.md_categories.map((s) => (
|
||||
<Select.Option key={s} value={s}>
|
||||
{s}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("jobs.fields.selling_dealer")}
|
||||
name="selling_dealer"
|
||||
@@ -131,8 +212,8 @@ export default function JobsDetailInsurance({ job, form }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
TODO: Adding servicing/selling dealer contact info?
|
||||
</FormRow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailGeneral);
|
||||
@@ -41,7 +41,7 @@ export function JobsDetailHeaderActions({
|
||||
const client = useApolloClient();
|
||||
const history = useHistory();
|
||||
const statusmenu = (
|
||||
<Menu key='popovermenu'>
|
||||
<Menu key="popovermenu">
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
logImEXEvent("job_header_schedule");
|
||||
@@ -53,16 +53,21 @@ export function JobsDetailHeaderActions({
|
||||
job: job,
|
||||
},
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.schedule")}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<Link to={`/manage/jobs/${job.id}/intake`}>
|
||||
{t("jobs.actions.intake")}
|
||||
</Link>
|
||||
<Menu.Item disabled={!!job.intakechecklist}>
|
||||
{!!job.intakechecklist ? (
|
||||
t("jobs.actions.intake")
|
||||
) : (
|
||||
<Link to={`/manage/jobs/${job.id}/intake`}>
|
||||
{t("jobs.actions.intake")}
|
||||
</Link>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key='enterpayments'
|
||||
key="enterpayments"
|
||||
onClick={() => {
|
||||
logImEXEvent("job_header_enter_payment");
|
||||
|
||||
@@ -70,29 +75,32 @@ export function JobsDetailHeaderActions({
|
||||
actions: {},
|
||||
context: { jobId: job.id },
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t("menus.header.enterpayment")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key='cccontract'>
|
||||
<Menu.Item key="cccontract">
|
||||
<Link
|
||||
to={{
|
||||
pathname: "/manage/courtesycars/contracts/new",
|
||||
state: { jobId: job.id },
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t("menus.jobsactions.newcccontract")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key='addtoproduction'
|
||||
key="addtoproduction"
|
||||
disabled={!!!job.converted || !!job.inproduction}
|
||||
onClick={() => AddToProduction(client, job.id, refetch)}>
|
||||
onClick={() => AddToProduction(client, job.id, refetch)}
|
||||
>
|
||||
{t("jobs.actions.addtoproduction")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key='duplicatejob'>
|
||||
<Menu.Item key="duplicatejob">
|
||||
<Popconfirm
|
||||
title={t("jobs.labels.duplicateconfirm")}
|
||||
okText='Yes'
|
||||
cancelText='No'
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onConfirm={() =>
|
||||
DuplicateJob(
|
||||
@@ -104,12 +112,13 @@ export function JobsDetailHeaderActions({
|
||||
}
|
||||
)
|
||||
}
|
||||
getPopupContainer={(trigger) => trigger.parentNode}>
|
||||
getPopupContainer={(trigger) => trigger.parentNode}
|
||||
>
|
||||
{t("menus.jobsactions.duplicate")}
|
||||
</Popconfirm>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key='postinvoices'
|
||||
key="postinvoices"
|
||||
onClick={() => {
|
||||
logImEXEvent("job_header_enter_invoice");
|
||||
|
||||
@@ -119,20 +128,22 @@ export function JobsDetailHeaderActions({
|
||||
job: job,
|
||||
},
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.postInvoices")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key='closejob'>
|
||||
<Menu.Item key="closejob">
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/manage/jobs/${job.id}/close`,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t("menus.jobsactions.closejob")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<JobsDetaiLheaderCsi job={job} />
|
||||
<Menu.Item
|
||||
key='jobcosting'
|
||||
key="jobcosting"
|
||||
onClick={() => {
|
||||
logImEXEvent("job_header_job_costing");
|
||||
|
||||
@@ -142,17 +153,19 @@ export function JobsDetailHeaderActions({
|
||||
jobId: job.id,
|
||||
},
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t("jobs.labels.jobcosting")}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
return (
|
||||
<Dropdown
|
||||
className='imex-flex-row__margin'
|
||||
className="imex-flex-row__margin"
|
||||
overlay={statusmenu}
|
||||
trigger={["click"]}
|
||||
key='changestatus'>
|
||||
key="changestatus"
|
||||
>
|
||||
<Button>
|
||||
{t("general.labels.actions")} <DownCircleFilled />
|
||||
</Button>
|
||||
|
||||
@@ -39,8 +39,6 @@ export default function DuplicateJob(
|
||||
variables: { job: [existingJob] },
|
||||
})
|
||||
.then((res2) => {
|
||||
console.log("res2", res2);
|
||||
|
||||
if (completionCallback)
|
||||
completionCallback(res2.data.insert_jobs.returning[0].id);
|
||||
});
|
||||
|
||||
@@ -1,27 +1,17 @@
|
||||
import { DownCircleFilled, PrinterFilled } from "@ant-design/icons";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Descriptions,
|
||||
Dropdown,
|
||||
Menu,
|
||||
notification,
|
||||
PageHeader,
|
||||
Tag,
|
||||
} from "antd";
|
||||
import { Button, Dropdown, Menu, PageHeader, Tag } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Moment from "react-moment";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
|
||||
import JobsConvertButton from "../jobs-convert-button/jobs-convert-button.component";
|
||||
import JobsDetailHeaderActions from "../jobs-detail-header-actions/jobs-detail-header-actions.component";
|
||||
import OwnerTagPopoverComponent from "../owner-tag-popover/owner-tag-popover.component";
|
||||
import VehicleTagPopoverComponent from "../vehicle-tag-popover/vehicle-tag-popover.component";
|
||||
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
|
||||
import "./jobs-detail-header.styles.scss";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -35,12 +25,13 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
export function JobsDetailHeader({
|
||||
job,
|
||||
mutationConvertJob,
|
||||
refetch,
|
||||
setPrintCenterContext,
|
||||
bodyshop,
|
||||
updateJobStatus,
|
||||
setScheduleContext,
|
||||
loading,
|
||||
form,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -48,7 +39,8 @@ export function JobsDetailHeader({
|
||||
<Menu
|
||||
onClick={(e) => {
|
||||
updateJobStatus(e.key);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{bodyshop.md_ro_statuses.statuses.map((item) => (
|
||||
<Menu.Item key={item}>{item}</Menu.Item>
|
||||
))}
|
||||
@@ -56,19 +48,20 @@ export function JobsDetailHeader({
|
||||
);
|
||||
|
||||
const menuExtra = (
|
||||
<div className='imex-flex-row'>
|
||||
<div className="imex-flex-row">
|
||||
<Dropdown
|
||||
className='imex-flex-row__margin'
|
||||
className="imex-flex-row__margin"
|
||||
overlay={statusmenu}
|
||||
trigger={["click"]}
|
||||
key='changestatus'>
|
||||
key="changestatus"
|
||||
>
|
||||
<Button>
|
||||
{t("jobs.actions.changestatus")} <DownCircleFilled />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
|
||||
<Button
|
||||
className='imex-flex-row__margin'
|
||||
className="imex-flex-row__margin"
|
||||
onClick={() => {
|
||||
setPrintCenterContext({
|
||||
actions: { refetch: refetch },
|
||||
@@ -78,35 +71,19 @@ export function JobsDetailHeader({
|
||||
},
|
||||
});
|
||||
}}
|
||||
key='printing'>
|
||||
key="printing"
|
||||
>
|
||||
<PrinterFilled />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
<JobsConvertButton job={job} refetch={refetch} />
|
||||
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
||||
<Button
|
||||
key='convert'
|
||||
className='imex-flex-row__margin'
|
||||
type='danger'
|
||||
style={{ display: job.converted ? "none" : "" }}
|
||||
disabled={job.converted}
|
||||
onClick={() => {
|
||||
mutationConvertJob({
|
||||
variables: { jobId: job.id },
|
||||
}).then((r) => {
|
||||
refetch();
|
||||
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.converted"),
|
||||
});
|
||||
});
|
||||
}}>
|
||||
{t("jobs.actions.convert")}
|
||||
</Button>
|
||||
<JobsDetailHeaderActions key='actions' job={job} refetch={refetch} />
|
||||
<Button
|
||||
type='primary'
|
||||
className='imex-flex-row__margin'
|
||||
key='submit'
|
||||
htmlType='submit'>
|
||||
type="primary"
|
||||
loading={loading}
|
||||
className="imex-flex-row__margin"
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -121,59 +98,26 @@ export function JobsDetailHeader({
|
||||
}
|
||||
subTitle={job.status}
|
||||
tags={[
|
||||
<OwnerTagPopoverComponent key='owner' job={job} />,
|
||||
<VehicleTagPopoverComponent key='vehicle' job={job} />,
|
||||
<OwnerTagPopoverComponent key="owner" job={job} />,
|
||||
<VehicleTagPopoverComponent key="vehicle" job={job} />,
|
||||
<Tag
|
||||
color='#f50'
|
||||
key='production'
|
||||
style={{ display: job.inproduction ? "" : "none" }}>
|
||||
color="#f50"
|
||||
key="production"
|
||||
style={{ display: job.inproduction ? "" : "none" }}
|
||||
>
|
||||
{t("jobs.labels.inproduction")}
|
||||
</Tag>,
|
||||
]}
|
||||
extra={menuExtra}>
|
||||
<Descriptions
|
||||
size='small'
|
||||
column={{ xs: 1, sm: 1, md: 2, lg: 3, xl: 3, xxl: 6 }}>
|
||||
<Descriptions.Item key='total' label={t("jobs.fields.repairtotal")}>
|
||||
<Tag title={t("jobs.fields.repairtotal")} key="total" color="green">
|
||||
<CurrencyFormatter>{job.clm_total}</CurrencyFormatter>
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item
|
||||
key='custowing'
|
||||
label={t("jobs.fields.customerowing")}>
|
||||
<span style={{ margin: "0rem .5rem" }}>/</span>
|
||||
<CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item
|
||||
key='scp'
|
||||
label={t("jobs.fields.specialcoveragepolicy")}>
|
||||
<Checkbox checked={job.special_coverage_policy} />
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item
|
||||
key='sched_comp'
|
||||
label={t("jobs.fields.scheduled_completion")}>
|
||||
{job.scheduled_completion ? (
|
||||
<Moment format='MM/DD/YYYY'>{job.scheduled_completion}</Moment>
|
||||
) : null}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item key='servicecar' label={t("jobs.fields.servicecar")}>
|
||||
{job.cccontracts &&
|
||||
job.cccontracts.map((item) => (
|
||||
<Link
|
||||
key={item.id}
|
||||
to={`/manage/courtesycars/contracts/${item.id}`}>
|
||||
<div>{`${item.agreementnumber} - ${item.start} - ${item.scheduledreturn}`}</div>
|
||||
</Link>
|
||||
))}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
key='assignments'
|
||||
label={t("jobs.labels.employeeassignments")}>
|
||||
<JobEmployeeAssignments job={job} />
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Tag>,
|
||||
]}
|
||||
extra={menuExtra}
|
||||
>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<JobEmployeeAssignments job={job} />
|
||||
</div>
|
||||
</PageHeader>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import { Form, Select } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function JobsDetailRates({ job, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormRow>
|
||||
<Form.Item label={t("jobs.fields.class")} name="class">
|
||||
<Select disabled={true} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.depreciation_taxes")}
|
||||
name="depreciation_taxes"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.other_amount_payable")}
|
||||
name="other_amount_payable"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.towing_payable")}
|
||||
name="towing_payable"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.storage_payable")}
|
||||
name="storage_payable"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.federal_tax_payable")}
|
||||
name="federal_tax_payable"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.adjustment_bottom_line")}
|
||||
name="adjustment_bottom_line"
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
<FormRow header={t("jobs.forms.laborrates")}>
|
||||
<Form.Item label={t("jobs.fields.rate_laa")} name="rate_laa">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lad")} name="rate_lad">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lae")} name="rate_lae">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lar")} name="rate_lar">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_las")} name="rate_las">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_laf")} name="rate_laf">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lam")} name="rate_lam">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lag")} name="rate_lag">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la1")} name="rate_la1">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la2")} name="rate_la2">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la3")} name="rate_la3">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_la4")} name="rate_la4">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_atp")} name="rate_atp">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_lau")} name="rate_lau">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mapa")} name="rate_mapa">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mash")} name="rate_mash">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mahw")} name="rate_mahw">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_ma2s")} name="rate_ma2s">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_ma3s")} name="rate_ma3s">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_mabl")} name="rate_mabl">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_macs")} name="rate_macs">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.rate_matd")} name="rate_matd">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(JobsDetailRates);
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Divider, Typography } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import JobTotalsTable from "../job-totals-table/job-totals-table.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const stripeTestEnv = process.env.REACT_APP_STRIPE_PUBLIC_KEY; //.includes("test");
|
||||
|
||||
export function JobsDetailTotals({ job, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography.Title level={4}>
|
||||
{t("payments.labels.title")}
|
||||
</Typography.Title>
|
||||
<table style={{ width: "100%" }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t("payments.fields.created_at")}</th>
|
||||
<th>{t("payments.fields.payer")}</th>
|
||||
<th>{t("payments.fields.amount")}</th>
|
||||
<th>{t("payments.fields.memo")}</th>
|
||||
<th>{t("payments.fields.type")}</th>
|
||||
<th>{t("payments.fields.transactionid")}</th>
|
||||
<th>{t("payments.fields.stripeid")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{job.payments.map((p, idx) => (
|
||||
<tr key={idx}>
|
||||
<td>
|
||||
<DateTimeFormatter>{p.created_at}</DateTimeFormatter>
|
||||
</td>
|
||||
<td>{p.payer}</td>
|
||||
<td>
|
||||
<CurrencyFormatter>{p.amount}</CurrencyFormatter>
|
||||
</td>
|
||||
<td>{p.memo}</td>
|
||||
<td>{p.type}</td>
|
||||
<td>{p.transactionid}</td>
|
||||
<td>
|
||||
{p.stripeid ? (
|
||||
<a
|
||||
href={
|
||||
stripeTestEnv
|
||||
? `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/test/payments/${p.stripeid}`
|
||||
: `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/payments/${p.stripeid}`
|
||||
}
|
||||
>
|
||||
{p.stripeid}
|
||||
</a>
|
||||
) : null}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Divider />
|
||||
|
||||
<JobTotalsTable job={job} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(JobsDetailTotals);
|
||||
@@ -38,7 +38,6 @@ export function JobsExportAllButton({
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
|
||||
@@ -43,11 +43,15 @@ export default function JobsFindModalComponent({
|
||||
render: (text, record) => {
|
||||
return record.owner ? (
|
||||
<Link to={"/manage/owners/" + record.owner.id}>
|
||||
{record.ownr_fn} {record.ownr_ln}
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
// t("jobs.errors.noowner")
|
||||
<span>{`${record.ownr_fn} ${record.ownr_ln}`}</span>
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -143,7 +147,8 @@ export default function JobsFindModalComponent({
|
||||
<Button
|
||||
onClick={() => {
|
||||
jobsListRefetch();
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Input
|
||||
@@ -154,10 +159,10 @@ export default function JobsFindModalComponent({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
size='small'
|
||||
size="small"
|
||||
pagination={{ position: "bottom" }}
|
||||
columns={columns.map((item) => ({ ...item }))}
|
||||
rowKey='id'
|
||||
rowKey="id"
|
||||
loading={jobsListLoading}
|
||||
dataSource={jobsList}
|
||||
rowSelection={{
|
||||
@@ -183,7 +188,8 @@ export default function JobsFindModalComponent({
|
||||
...importOptions,
|
||||
overrideHeaders: e.target.checked,
|
||||
})
|
||||
}>
|
||||
}
|
||||
>
|
||||
{t("jobs.labels.override_header")}
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
@@ -55,10 +55,14 @@ export default function JobsList({ refetch, loading, jobs, total }) {
|
||||
render: (text, record) => {
|
||||
return record.owner ? (
|
||||
<Link to={"/manage/owners/" + record.owner.id}>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""}`}
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""}`}</span>
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -176,7 +180,7 @@ export default function JobsList({ refetch, loading, jobs, total }) {
|
||||
<div>
|
||||
<Table
|
||||
loading={loading}
|
||||
size='small'
|
||||
size="small"
|
||||
scroll={{ x: true }}
|
||||
pagination={{
|
||||
position: "top",
|
||||
@@ -185,17 +189,17 @@ export default function JobsList({ refetch, loading, jobs, total }) {
|
||||
total: total,
|
||||
}}
|
||||
columns={columns}
|
||||
rowKey='id'
|
||||
rowKey="id"
|
||||
dataSource={jobs}
|
||||
onChange={handleTableChange}
|
||||
title={() => {
|
||||
return (
|
||||
<div className='imex-table-header'>
|
||||
<div className="imex-table-header">
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Input.Search
|
||||
className='imex-table-header__search'
|
||||
className="imex-table-header__search"
|
||||
placeholder={t("general.labels.search")}
|
||||
onSearch={(value) => {
|
||||
search.search = value;
|
||||
|
||||
@@ -39,7 +39,7 @@ export function JobsList({ bodyshop }) {
|
||||
const history = useHistory();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
const jobs = data
|
||||
? searchText === ""
|
||||
@@ -50,6 +50,9 @@ export function JobsList({ bodyshop }) {
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
@@ -122,10 +125,14 @@ export function JobsList({ bodyshop }) {
|
||||
render: (text, record) => {
|
||||
return record.owner ? (
|
||||
<Link to={"/manage/owners/" + record.owner.id}>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""}`}
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""}`}</span>
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -242,21 +249,21 @@ export function JobsList({ bodyshop }) {
|
||||
return (
|
||||
<Table
|
||||
loading={loading}
|
||||
size='small'
|
||||
size="small"
|
||||
pagination={false}
|
||||
columns={columns}
|
||||
rowKey='id'
|
||||
rowKey="id"
|
||||
dataSource={jobs}
|
||||
style={{ height: "100%" }}
|
||||
scroll={{ x: true }}
|
||||
title={() => {
|
||||
return (
|
||||
<div className='imex-table-header'>
|
||||
<div className="imex-table-header">
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Input.Search
|
||||
className='imex-table-header__search'
|
||||
className="imex-table-header__search"
|
||||
placeholder={t("general.labels.search")}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
|
||||
16
client/src/components/not-found/not-found.component.jsx
Normal file
16
client/src/components/not-found/not-found.component.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
import { Result } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function NotFound() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<Result
|
||||
status="404"
|
||||
title={t("general.messages.notfoundtitle")}
|
||||
subTitle={t("general.messages.notfoundsub")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,35 +6,37 @@ import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import FormItemPhone from "../form-items-formatted/phone-form-item.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
export default function OwnerDetailFormComponent({ form }) {
|
||||
export default function OwnerDetailFormComponent({ form, loading }) {
|
||||
const { t } = useTranslation();
|
||||
const { getFieldValue } = form;
|
||||
return (
|
||||
<div>
|
||||
<div className='imex-flex-row imex-flex-row__flex-space-around'>
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<Button
|
||||
className='imex-flex-row__margin-large'
|
||||
type='primary'
|
||||
key='submit'
|
||||
htmlType='submit'>
|
||||
className="imex-flex-row__margin-large"
|
||||
type="primary"
|
||||
key="submit"
|
||||
loading={loading}
|
||||
htmlType="submit"
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<div className='imex-flex-row__grow imex-flex-row__margin-large'>
|
||||
<div className="imex-flex-row__grow imex-flex-row__margin-large">
|
||||
<FormFieldsChanged form={form} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LayoutFormRow header={t("owners.forms.name")}>
|
||||
<Form.Item label={t("owners.fields.ownr_title")} name='ownr_title'>
|
||||
<Form.Item label={t("owners.fields.ownr_title")} name="ownr_title">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("owners.fields.ownr_ln")} name='ownr_ln'>
|
||||
<Form.Item label={t("owners.fields.ownr_ln")} name="ownr_ln">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("owners.fields.ownr_fn")} name='ownr_fn'>
|
||||
<Form.Item label={t("owners.fields.ownr_fn")} name="ownr_fn">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("owners.fields.ownr_co_nm")} name='ownr_co_nm'>
|
||||
<Form.Item label={t("owners.fields.ownr_co_nm")} name="ownr_co_nm">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
@@ -42,52 +44,56 @@ export default function OwnerDetailFormComponent({ form }) {
|
||||
<LayoutFormRow header={t("owners.forms.address")}>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_addr1")}
|
||||
name='ownr_addr1'
|
||||
name="ownr_addr1"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}>
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("owners.fields.ownr_addr2")} name='ownr_addr2'>
|
||||
<Form.Item label={t("owners.fields.ownr_addr2")} name="ownr_addr2">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_city")}
|
||||
name='ownr_city'
|
||||
name="ownr_city"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}>
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_st")}
|
||||
name='ownr_st'
|
||||
name="ownr_st"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}>
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_zip")}
|
||||
name='ownr_zip'
|
||||
name="ownr_zip"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}>
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("owners.fields.ownr_ctry")} name='ownr_ctry'>
|
||||
<Form.Item label={t("owners.fields.ownr_ctry")} name="ownr_ctry">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
@@ -95,27 +101,30 @@ export default function OwnerDetailFormComponent({ form }) {
|
||||
<LayoutFormRow header={t("owners.forms.contact")}>
|
||||
<Form.Item
|
||||
label={t("owners.fields.allow_text_message")}
|
||||
name='allow_text_message'
|
||||
valuePropName='checked'>
|
||||
name="allow_text_message"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.ownr_ea")}
|
||||
name='ownr_ea'
|
||||
name="ownr_ea"
|
||||
rules={[
|
||||
{
|
||||
type: "email",
|
||||
message: "This is not a valid email address.",
|
||||
},
|
||||
]}>
|
||||
]}
|
||||
>
|
||||
<FormItemEmail email={getFieldValue("ownr_ea")} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("owners.fields.ownr_ph1")} name='ownr_ph1'>
|
||||
<Form.Item label={t("owners.fields.ownr_ph1")} name="ownr_ph1">
|
||||
<FormItemPhone customInput={Input} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("owners.fields.preferred_contact")}
|
||||
name='preferred_contact'>
|
||||
name="preferred_contact"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Form, notification } from "antd";
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { useMutation } from "@apollo/react-hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_OWNER } from "../../graphql/owners.queries";
|
||||
@@ -8,10 +8,11 @@ import OwnerDetailFormComponent from "./owner-detail-form.component";
|
||||
function OwnerDetailFormContainer({ owner, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateOwner] = useMutation(UPDATE_OWNER);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const result = await updateOwner({
|
||||
variables: { ownerId: owner.id, owner: values },
|
||||
});
|
||||
@@ -32,6 +33,7 @@ function OwnerDetailFormContainer({ owner, refetch }) {
|
||||
if (refetch) await refetch();
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -42,7 +44,7 @@ function OwnerDetailFormContainer({ owner, refetch }) {
|
||||
layout="vertical"
|
||||
initialValues={owner}
|
||||
>
|
||||
<OwnerDetailFormComponent form={form} />
|
||||
<OwnerDetailFormComponent loading={loading} form={form} />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function OwnerFindModalComponent({
|
||||
selectedOwner,
|
||||
setSelectedOwner,
|
||||
ownersListLoading,
|
||||
ownersList
|
||||
ownersList,
|
||||
}) {
|
||||
//setSelectedOwner is used to set the record id of the owner to use for adding the job.
|
||||
const { t } = useTranslation();
|
||||
@@ -16,27 +16,32 @@ export default function OwnerFindModalComponent({
|
||||
{
|
||||
title: t("owners.fields.ownr_ln"),
|
||||
dataIndex: "ownr_ln",
|
||||
key: "ownr_ln"
|
||||
key: "ownr_ln",
|
||||
},
|
||||
{
|
||||
title: t("owners.fields.ownr_fn"),
|
||||
dataIndex: "ownr_fn",
|
||||
key: "ownr_fn"
|
||||
key: "ownr_fn",
|
||||
},
|
||||
{
|
||||
title: t("owners.fields.ownr_co_nm"),
|
||||
dataIndex: "ownr_co_nm",
|
||||
key: "ownr_co_nm",
|
||||
},
|
||||
{
|
||||
title: t("owners.fields.ownr_addr1"),
|
||||
dataIndex: "ownr_addr1",
|
||||
key: "ownr_addr1"
|
||||
key: "ownr_addr1",
|
||||
},
|
||||
{
|
||||
title: t("owners.fields.ownr_city"),
|
||||
dataIndex: "ownr_city",
|
||||
key: "ownr_city"
|
||||
key: "ownr_city",
|
||||
},
|
||||
{
|
||||
title: t("owners.fields.ownr_ea"),
|
||||
dataIndex: "ownr_ea",
|
||||
key: "ownr_ea"
|
||||
key: "ownr_ea",
|
||||
},
|
||||
{
|
||||
title: t("owners.fields.ownr_ph1"),
|
||||
@@ -44,11 +49,11 @@ export default function OwnerFindModalComponent({
|
||||
key: "ownr_ph1",
|
||||
render: (text, record) => (
|
||||
<PhoneFormatter>{record.ownr_ph1}</PhoneFormatter>
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const handleOnRowClick = record => {
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
if (record.id) {
|
||||
setSelectedOwner(record.id);
|
||||
@@ -64,22 +69,22 @@ export default function OwnerFindModalComponent({
|
||||
title={() => t("owners.labels.existing_owners")}
|
||||
size="small"
|
||||
pagination={{ position: "bottom" }}
|
||||
columns={columns.map(item => ({ ...item }))}
|
||||
columns={columns.map((item) => ({ ...item }))}
|
||||
rowKey="id"
|
||||
loading={ownersListLoading}
|
||||
dataSource={ownersList}
|
||||
rowSelection={{
|
||||
onSelect: props => {
|
||||
onSelect: (props) => {
|
||||
setSelectedOwner(props.id);
|
||||
},
|
||||
type: "radio",
|
||||
selectedRowKeys: [selectedOwner]
|
||||
selectedRowKeys: [selectedOwner],
|
||||
}}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: event => {
|
||||
onClick: (event) => {
|
||||
handleOnRowClick(record);
|
||||
}
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -20,27 +20,28 @@ export default function OwnerFindModalContainer({
|
||||
|
||||
const ownersList = useQuery(QUERY_SEARCH_OWNER_BY_IDX, {
|
||||
variables: {
|
||||
search: owner ? `${owner.ownr_fn || ""} ${owner.ownr_ln || ""}` : null
|
||||
search: owner ? `${owner.ownr_fn || ""} ${owner.ownr_ln || ""}` : null,
|
||||
},
|
||||
skip: !owner,
|
||||
fetchPolicy: "network-only"
|
||||
fetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t("owners.labels.existing_owners")}
|
||||
width={"80%"}
|
||||
{...modalProps}>
|
||||
{...modalProps}
|
||||
>
|
||||
{loading ? <LoadingSpinner /> : null}
|
||||
{error ? <AlertComponent message={error.message} type='error' /> : null}
|
||||
{error ? <AlertComponent message={error.message} type="error" /> : null}
|
||||
{owner ? (
|
||||
<OwnerFindModalComponent
|
||||
selectedOwner={selectedOwner}
|
||||
setSelectedOwner={setSelectedOwner}
|
||||
ownersListLoading={ownersList.loading}
|
||||
ownersList={
|
||||
ownersList.data && ownersList.data.search_owner
|
||||
? ownersList.data.search_owner
|
||||
ownersList.data && ownersList.data.search_owners
|
||||
? ownersList.data.search_owners
|
||||
: null
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -33,7 +33,9 @@ export default function OwnersListComponent({
|
||||
key: "name",
|
||||
render: (text, record) => (
|
||||
<Link to={"/manage/owners/" + record.id}>
|
||||
{`${record.ownr_fn} ${record.ownr_ln}`}
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -5,11 +5,13 @@ import { useTranslation } from "react-i18next";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
|
||||
export default function PartsOrderModalComponent({
|
||||
vendorList,
|
||||
sendTypeState,
|
||||
isReturn,
|
||||
preferredMake,
|
||||
}) {
|
||||
const [sendType, setSendType] = sendTypeState;
|
||||
|
||||
@@ -27,7 +29,11 @@ export default function PartsOrderModalComponent({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<VendorSearchSelect options={vendorList} disabled={isReturn} />
|
||||
<VendorSearchSelect
|
||||
options={vendorList}
|
||||
disabled={isReturn}
|
||||
preferredMake={preferredMake}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="deliver_by"
|
||||
@@ -44,7 +50,7 @@ export default function PartsOrderModalComponent({
|
||||
{t("parts_orders.labels.inthisorder")}
|
||||
|
||||
<Form.List name={["parts_order_lines", "data"]}>
|
||||
{(fields, { add, remove }) => {
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
@@ -96,6 +102,11 @@ export default function PartsOrderModalComponent({
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
<FormListMoveArrows
|
||||
move={move}
|
||||
index={index}
|
||||
total={fields.length}
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
))}
|
||||
|
||||
@@ -63,7 +63,9 @@ export function PartsOrderModalContainer({
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_ALL_VENDORS_FOR_ORDER, {
|
||||
skip: !visible,
|
||||
variables: { jobId: jobId },
|
||||
});
|
||||
|
||||
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
|
||||
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
|
||||
const [insertInvoice] = useMutation(INSERT_NEW_INVOICE);
|
||||
@@ -111,7 +113,6 @@ export function PartsOrderModalContainer({
|
||||
});
|
||||
|
||||
if (values.vendorid === bodyshop.inhousevendorid) {
|
||||
console.log("Inhouse Invoice needs to be psoted.");
|
||||
logImEXEvent("parts_order_inhouse_invoice");
|
||||
|
||||
let invoiceToPost = {
|
||||
@@ -233,6 +234,7 @@ export function PartsOrderModalContainer({
|
||||
vendorList={(data && data.vendors) || []}
|
||||
sendTypeState={sendTypeState}
|
||||
isReturn={isReturn}
|
||||
preferredMake={data && data.jobs[0] && data.jobs[0].v_make_desc}
|
||||
/>
|
||||
</Form>
|
||||
</LoadingSpinner>
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import React from "react";
|
||||
|
||||
export default function PartsStatusPie({ partsList }) {
|
||||
return <div>Parts Pie</div>;
|
||||
//const [pieData, setPieData] = useState([]);
|
||||
|
||||
const result = partsList
|
||||
? partsList.reduce((names, name) => {
|
||||
const val = name || "?";
|
||||
const count = names[val] || 0;
|
||||
names[val] = count + 1;
|
||||
return names;
|
||||
}, {})
|
||||
: {};
|
||||
// const result = partsList
|
||||
// ? partsList.reduce((names, name) => {
|
||||
// const val = name || "?";
|
||||
// const count = names[val] || 0;
|
||||
// names[val] = count + 1;
|
||||
// return names;
|
||||
// }, {})
|
||||
// : {};
|
||||
|
||||
const pieData = Object.keys(result).map((i) => {
|
||||
console.log("i", i);
|
||||
return {
|
||||
id: i,
|
||||
label: i,
|
||||
value: result[i],
|
||||
};
|
||||
});
|
||||
// const pieData = Object.keys(result).map((i) => {
|
||||
// return {
|
||||
// id: i,
|
||||
// label: i,
|
||||
// value: result[i],
|
||||
// };
|
||||
// });
|
||||
|
||||
return <div>{JSON.stringify(pieData)}</div>;
|
||||
// return <div>{JSON.stringify(pieData)}</div>;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ export function PaymentFormComponent({
|
||||
|
||||
const { t } = useTranslation();
|
||||
const handleStripeChange = (e) => {
|
||||
console.log("e", e);
|
||||
setStripeState({ error: e.error, cardComplete: e.complete });
|
||||
};
|
||||
|
||||
|
||||
@@ -88,7 +88,6 @@ function InvoiceEnterModalContainer({
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handleFinish -> stripePayment", stripePayment);
|
||||
|
||||
if (stripePayment.paymentIntent.status === "succeeded") {
|
||||
notification["success"]({ message: t("payments.successes.stripe") });
|
||||
@@ -172,13 +171,15 @@ function InvoiceEnterModalContainer({
|
||||
okButtonProps={{
|
||||
loading: loading,
|
||||
}}
|
||||
destroyOnClose>
|
||||
destroyOnClose
|
||||
>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
autoComplete={"off"}
|
||||
form={form}
|
||||
layout='vertical'
|
||||
initialValues={{ jobid: context.jobId }}>
|
||||
layout="vertical"
|
||||
initialValues={{ jobid: context.jobId }}
|
||||
>
|
||||
<PaymentForm form={form} stripeStateArr={stripeStateArr} />
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@@ -38,7 +38,6 @@ export function PaymentsExportAllButton({
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
|
||||
@@ -56,11 +56,13 @@ export default function PaymentsListPaginated({
|
||||
render: (text, record) => {
|
||||
return record.job.owner ? (
|
||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""}`}
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.job.ownr_fn || ""} ${
|
||||
record.job.ownr_ln || ""
|
||||
<span>{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Input } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import EmployeeSearchSelectComponent from "../employee-search-select/employee-search-select.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ProductionBoardFilters);
|
||||
|
||||
export function ProductionBoardFilters({ bodyshop, filter, setFilter }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Input.Search
|
||||
//value={filter.search}
|
||||
placeholder={t("general.labels.search")}
|
||||
onChange={(e) => {
|
||||
setFilter({ ...filter, search: e.target.value });
|
||||
}}
|
||||
/>
|
||||
<EmployeeSearchSelectComponent
|
||||
options={bodyshop.employees}
|
||||
value={filter.employeeId}
|
||||
onChange={(emp) => setFilter({ ...filter, employeeId: emp })}
|
||||
allowClear
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,74 +1,87 @@
|
||||
import React from "react";
|
||||
import { Card, Row, Col } from "antd";
|
||||
import { Card, Row, Col, Dropdown } from "antd";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import ProductionAlert from "../production-list-columns/production-list-columns.alert.component";
|
||||
import { EyeFilled } from "@ant-design/icons";
|
||||
import { Link } from "react-router-dom";
|
||||
import "./production-board-card.styles.scss";
|
||||
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function ProductionBoardCard(card) {
|
||||
const { t } = useTranslation();
|
||||
const menu = (
|
||||
<div>
|
||||
<Card title={t("general.labels.actions")}>
|
||||
<ProductionRemoveButton jobId={card.id} />
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
className="react-kanban-card imex-kanban-card tight-antd-rows"
|
||||
style={{ margin: ".2rem 0rem" }}
|
||||
size="small"
|
||||
title={`${card.ro_number || card.est_number} - ${card.v_model_yr} ${
|
||||
card.v_make_desc || ""
|
||||
} ${card.v_model_desc || ""}`}
|
||||
>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<div className="ellipses">{`${card.ownr_fn || ""} ${
|
||||
card.ownr_ln || ""
|
||||
} ${card.ownr_co_nm || ""}`}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<div className="ellipses">{card.clm_no || ""}</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div className="ellipses">{card.ins_co_nm || ""}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<div className="mex-flex-row__margin">
|
||||
<div>{`B: ${card.labhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div>
|
||||
<div>{`R: ${card.labhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div>
|
||||
<Dropdown overlay={menu} trigger={["contextMenu"]}>
|
||||
<Card
|
||||
className="react-kanban-card imex-kanban-card tight-antd-rows"
|
||||
style={{ margin: ".2rem 0rem" }}
|
||||
size="small"
|
||||
title={`${card.ro_number || card.est_number} - ${card.v_model_yr} ${
|
||||
card.v_make_desc || ""
|
||||
} ${card.v_model_desc || ""}`}
|
||||
>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<div className="ellipses">{`${card.ownr_fn || ""} ${
|
||||
card.ownr_ln || ""
|
||||
} ${card.ownr_co_nm || ""}`}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<div className="ellipses">{card.clm_no || ""}</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div className="ellipses">{card.ins_co_nm || ""}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<div className="mex-flex-row__margin">
|
||||
<div>{`B: ${card.labhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div>
|
||||
<div>{`R: ${card.larhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div>
|
||||
</div>
|
||||
<div className="mex-flex-row__margin">
|
||||
<div>{`B: ${
|
||||
card.employee_body_rel
|
||||
? `${card.employee_body_rel.first_name} ${card.employee_body_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
<div>{`P: ${
|
||||
card.employee_prep_rel
|
||||
? `${card.employee_prep_rel.first_name} ${card.employee_prep_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
<div>{`R: ${
|
||||
card.employee_refinish_rel
|
||||
? `${card.employee_refinish_rel.first_name} ${card.employee_refinish_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mex-flex-row__margin">
|
||||
<div>{`B: ${
|
||||
card.employee_body_rel
|
||||
? `${card.employee_body_rel.first_name} ${card.employee_body_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
<div>{`P: ${
|
||||
card.employee_prep_rel
|
||||
? `${card.employee_prep_rel.first_name} ${card.employee_prep_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
<div>{`R: ${
|
||||
card.employee_refinish_rel
|
||||
? `${card.employee_refinish_rel.first_name} ${card.employee_refinish_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<DateTimeFormatter>{card.scheduled_completion}</DateTimeFormatter>
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<ProductionAlert record={card} key="alert" />
|
||||
<Link to={`/manage/jobs/${card.id}`}>
|
||||
<EyeFilled key="setting" />
|
||||
</Link>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<DateTimeFormatter>{card.scheduled_completion}</DateTimeFormatter>
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<ProductionAlert record={card} key="alert" />
|
||||
<Link to={`/manage/jobs/${card.id}`}>
|
||||
<EyeFilled key="setting" />
|
||||
</Link>
|
||||
</div>
|
||||
</Card>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import ProductionBoardCard from "../production-board-kanban-card/production-boar
|
||||
import { createBoardData } from "./production-board-kanban.utils.js";
|
||||
import IndefiniteLoading from "../indefinite-loading/indefinite-loading.component";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -22,12 +23,14 @@ export function ProductionBoardKanbanComponent({ data, bodyshop }) {
|
||||
columns: [{ id: "Loading...", title: "Loading...", cards: [] }],
|
||||
});
|
||||
|
||||
const [filter, setFilter] = useState({ search: "", employeeId: null });
|
||||
|
||||
const [isMoving, setIsMoving] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
setBoardLanes(
|
||||
createBoardData(bodyshop.md_ro_statuses.production_statuses, data)
|
||||
createBoardData(bodyshop.md_ro_statuses.production_statuses, data, filter)
|
||||
);
|
||||
setIsMoving(false);
|
||||
}, [
|
||||
@@ -35,6 +38,7 @@ export function ProductionBoardKanbanComponent({ data, bodyshop }) {
|
||||
setBoardLanes,
|
||||
setIsMoving,
|
||||
bodyshop.md_ro_statuses.production_statuses,
|
||||
filter,
|
||||
]);
|
||||
|
||||
const client = useApolloClient();
|
||||
@@ -76,13 +80,13 @@ export function ProductionBoardKanbanComponent({ data, bodyshop }) {
|
||||
|
||||
let movedCardNewKanbanParent;
|
||||
if (movedCardWillBeFirst) {
|
||||
console.log("==> New Card is first.");
|
||||
//console.log("==> New Card is first.");
|
||||
movedCardNewKanbanParent = "-1";
|
||||
} else if (movedCardWillBeLast) {
|
||||
console.log("==> New Card is last.");
|
||||
// console.log("==> New Card is last.");
|
||||
movedCardNewKanbanParent = lastCardInDestinationColumn.id;
|
||||
} else if (!!newChildCard) {
|
||||
console.log("==> New Card is somewhere in the middle");
|
||||
// console.log("==> New Card is somewhere in the middle");
|
||||
movedCardNewKanbanParent = newChildCard.kanbanparent;
|
||||
} else {
|
||||
throw new Error("==> !!!!!!Couldn't find a parent.!!!! <==");
|
||||
@@ -111,6 +115,7 @@ export function ProductionBoardKanbanComponent({ data, bodyshop }) {
|
||||
return (
|
||||
<div>
|
||||
<IndefiniteLoading loading={isMoving} />
|
||||
<ProductionBoardFilters filter={filter} setFilter={setFilter} />
|
||||
<Board
|
||||
children={boardLanes}
|
||||
disableCardDrag={isMoving}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user