Merge pull request #9 from snaptsoft/dev-patrick

Dev patrick
This commit is contained in:
2020-02-07 17:04:12 -08:00
committed by GitHub
57 changed files with 4026 additions and 1485 deletions

View File

@@ -1,12 +1,14 @@
React App:
React Hooks are used for Authentication ONLY to ensure the correct web token is passed.
React App:
Yarn Dependency Management:
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.
ALL CHANGES MUST BE MADE USING LOCAL CONSOLE TO ENSURE DATABASE MIGRATION FILES ARE CREATED.
Hasura is hosted on another dyno. Several environmental variables are required, including disabling the console.
ALL CHANGES MUST BE MADE USING LOCAL CONSOLE TO ENSURE DATABASE MIGRATION FILES ARE CREATED.
To Start Hasura CLI:
npx hasura console --admin-secret Dev-BodyShopAppBySnaptSoftware!
Migrating to Staging:
npx hasura migrate apply --up 10 --endpoint https://bodyshop-staging-db.herokuapp.com/ --admin-secret Staging-BodyShopAppBySnaptSoftware!
Migrating to Staging:
npx hasura migrate apply --up 10 --endpoint https://bodyshop-staging-db.herokuapp.com/ --admin-secret Staging-BodyShopAppBySnaptSoftware!

View File

@@ -1,4 +1,4 @@
<babeledit_project be_version="2.6.1" version="1.2">
<babeledit_project version="1.2" be_version="2.6.1">
<!--
BabelEdit project file
@@ -4038,6 +4038,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>customers</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>home</name>
<definition_loaded>false</definition_loaded>
@@ -4080,6 +4101,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>owners</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>schedule</name>
<definition_loaded>false</definition_loaded>
@@ -4101,6 +4143,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>vehicles</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>
@@ -4605,9 +4668,56 @@
<folder_node>
<name>owners</name>
<children>
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>noaccess</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>
<name>fields</name>
<children>
<concept_node>
<name>allow_text_message</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>ownr_addr1</name>
<definition_loaded>false</definition_loaded>
@@ -4629,6 +4739,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_addr2</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>ownr_city</name>
<definition_loaded>false</definition_loaded>
@@ -4650,6 +4781,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_ctry</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>ownr_ea</name>
<definition_loaded>false</definition_loaded>
@@ -4734,6 +4886,90 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_st</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>ownr_title</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>ownr_zip</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>preferred_contact</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>
@@ -4762,6 +4998,32 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>successes</name>
<children>
<concept_node>
<name>save</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>
@@ -4903,6 +5165,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>manageroot</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>profile</name>
<definition_loaded>false</definition_loaded>
@@ -4945,6 +5228,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>vehicledetail</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>
@@ -5021,6 +5325,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>photourl</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>
@@ -5028,6 +5353,74 @@
<folder_node>
<name>vehicles</name>
<children>
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>noaccess</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>validation</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>validationtitle</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>
<name>fields</name>
<children>
@@ -5052,6 +5445,431 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>plate_st</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>trim_color</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>v_bstyle</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>v_color</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>v_cond</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>v_engine</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>v_make_desc</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>v_makecode</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>v_mldgcode</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>v_model_desc</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>v_model_yr</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>v_options</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>v_paint_codes</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>v_prod_dt</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>v_stage</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>v_tone</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>v_trimcode</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>v_type</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>v_vin</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>
<name>successes</name>
<children>
<concept_node>
<name>save</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>

View File

@@ -4,39 +4,39 @@
"private": true,
"proxy": "https://localhost:5000",
"dependencies": {
"antd": "^3.26.0",
"antd": "^3.26.8",
"apollo-boost": "^0.4.4",
"apollo-link-context": "^1.0.19",
"apollo-link-error": "^1.1.12",
"apollo-link-logger": "^1.2.3",
"apollo-link-ws": "^1.0.19",
"axios": "^0.19.1",
"axios": "^0.19.2",
"chart.js": "^2.9.3",
"dotenv": "^8.2.0",
"firebase": "^7.5.0",
"graphql": "^14.5.8",
"i18next": "^19.0.2",
"node-sass": "^4.13.0",
"firebase": "^7.8.1",
"graphql": "^14.6.0",
"i18next": "^19.1.0",
"node-sass": "^4.13.1",
"react": "^16.12.0",
"react-apollo": "^3.1.3",
"react-big-calendar": "^0.23.0",
"react-chartjs-2": "^2.8.0",
"react-chartjs-2": "^2.9.0",
"react-dom": "^16.12.0",
"react-i18next": "^11.2.7",
"react-icons": "^3.8.0",
"react-i18next": "^11.3.1",
"react-icons": "^3.9.0",
"react-image-file-resizer": "^0.2.1",
"react-moment": "^0.9.7",
"react-number-format": "^4.3.1",
"react-redux": "^7.1.3",
"react-router-dom": "^5.1.2",
"react-scripts": "3.2.0",
"react-scripts": "3.3.1",
"react-trello": "^2.2.3",
"redux": "^4.0.5",
"redux-logger": "^3.0.6",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"reselect": "^4.0.0",
"styled-components": "^4.4.1",
"styled-components": "^5.0.1",
"subscriptions-transport-ws": "^0.9.16"
},
"scripts": {

View File

@@ -1,6 +1,7 @@
{
"short_name": "Bodyshop",
"short_name": "Bodyshop.app",
"name": "Bodyshop Management System",
"description": "The ultimate bodyshop management system",
"icons": [
{
"src": "favicon.ico",
@@ -20,6 +21,6 @@
],
"start_url": ".",
"display": "standalone",
"theme_color": "#002366",
"background_color": "#000000"
"theme_color": "#fff",
"background_color": "#fff"
}

View File

@@ -13,7 +13,7 @@ import PrivateRoute from "../utils/private-route";
import "./App.css";
const LandingPage = lazy(() => import("../pages/landing/landing.page"));
const ManagePage = lazy(() => import("../pages/manage/manage.page"));
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")
@@ -28,8 +28,7 @@ const mapDispatchToProps = dispatch => ({
export default connect(
mapStateToProps,
mapDispatchToProps
)(({ checkUserSession, currentUser, setBodyshop }) => {
)(({ checkUserSession, currentUser }) => {
useEffect(() => {
checkUserSession();
return () => {};

View File

@@ -1,79 +0,0 @@
import { Avatar, Col, Dropdown, Icon, Menu, Row } from "antd";
import i18next from "i18next";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import UserImage from "../../assets/User.svg";
import { setUserLanguage, signOutStart } from "../../redux/user/user.actions";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import SignOut from "../sign-out/sign-out.component";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser
});
const mapDispatchToProps = dispatch => ({
signOutStart: () => dispatch(signOutStart()),
setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(function CurrentUserDropdown({ currentUser, signOutStart, setUserLanguage }) {
const { t } = useTranslation();
const handleMenuClick = e => {
if (e.item.props.actiontype === "lang-select") {
i18next.changeLanguage(e.key, (err, t) => {
if (err)
return console.log("Error encountered when changing languages.", err);
setUserLanguage(e.key);
console.log("clicking");
});
}
};
const menu = (
<Menu mode="vertical" onClick={handleMenuClick}>
<Menu.Item>
<SignOut signOutStart={signOutStart} />
</Menu.Item>
<Menu.Item>
<Link to="/manage/profile"> {t("menus.currentuser.profile")}</Link>
</Menu.Item>
<Menu.SubMenu
title={
<span>
<Icon type="global" />
<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">
{t("general.languages.french")}
</Menu.Item>
<Menu.Item actiontype="lang-select" key="es">
{t("general.languages.spanish")}
</Menu.Item>
</Menu.SubMenu>
</Menu>
);
return (
<Dropdown overlay={menu}>
<Row>
<Col span={8}>
<Avatar size="large" alt="Avatar" src={UserImage} />
</Col>
<Col span={16} style={{ color: "white" }}>
{currentUser.displayName || t("general.labels.unknown")}
</Col>
</Row>
</Dropdown>
);
});

View File

@@ -4,7 +4,7 @@ import React from "react";
export default function FooterComponent() {
return (
<Row>
<Col span={8} offset={9}>
<Col span={8} offset={8}>
Copyright Snapt Software 2019. All rights reserved.
</Col>
</Row>

View File

@@ -5,9 +5,13 @@ function FormItemEmail(props, ref) {
<Input
{...props}
addonAfter={
<a href={`mailto:${props.email}`}>
props.email ? (
<a href={`mailto:${props.email}`}>
<Icon type="mail" />
</a>
) : (
<Icon type="mail" />
</a>
)
}
/>
);

View File

@@ -0,0 +1,21 @@
import { Button } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import AlertComponent from "../alert/alert.component";
export default function ResetForm({ resetFields }) {
const { t } = useTranslation();
return (
<AlertComponent
message={
<div>
{t("general.messages.unsavedchanges")}
<Button onClick={() => resetFields()}>
{t("general.actions.reset")}
</Button>
</div>
}
closable
/>
);
}

View File

@@ -1,79 +0,0 @@
import React from "react";
// import { Icon, Button, Input, AutoComplete } from "antd";
// const { Option } = AutoComplete;
// function onSelect(value) {
// console.log("onSelect", value);
// }
// function getRandomInt(max, min = 0) {
// return Math.floor(Math.random() * (max - min + 1)) + min; // eslint-disable-line no-mixed-operators
// }
// function searchResult(query) {
// return new Array(getRandomInt(5))
// .join(".")
// .split(".")
// .map((item, idx) => ({
// query,
// category: `${query}${idx}`,
// count: getRandomInt(200, 100)
// }));
// }
// function renderOption(item) {
// return (
// <Option key={item.category} text={item.category}>
// <div className='global-search-item'>
// <span className='global-search-item-desc'>
// Found {item.query} on
// <a
// href={`https://s.taobao.com/search?q=${item.query}`}
// target='_blank'
// rel='noopener noreferrer'>
// {item.category}
// </a>
// </span>
// <span className='global-search-item-count'>{item.count} results</span>
// </div>
// </Option>
// );
// }
export default class GlobalSearch extends React.Component {
state = {
dataSource: []
};
// handleSearch = value => {
// this.setState({
// dataSource: value ? searchResult(value) : []
// });
// };
render() {
return (
<div />
// <div style={{ width: 300 }}>
// <AutoComplete
// size="large"
// style={{ width: "100%" }}
// dataSource={dataSource.map(renderOption)}
// onSelect={onSelect}
// onSearch={this.handleSearch}
// placeholder="input here"
// optionLabelProp="text"
// >
// <Input
// suffix={
// <Button style={{ marginRight: -12 }} size="large" type="primary">
// <Icon type="search" />
// </Button>
// }
// />
// </AutoComplete>
// </div>
);
}
}

View File

@@ -1,27 +1,35 @@
import { Col, Icon, Menu, Row } from "antd";
import { Avatar, Col, Icon, Menu, Row } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import CurrentUserDropdown from "../current-user-dropdown/current-user-dropdown.component";
import GlobalSearch from "../global-search/global-search.component";
import UserImage from "../../assets/User.svg";
import { signOutStart } from "../../redux/user/user.actions";
import ManageSignInButton from "../manage-sign-in-button/manage-sign-in-button.component";
export default ({ landingHeader, selectedNavItem }) => {
export default ({
landingHeader,
selectedNavItem,
logo,
handleMenuClick,
currentUser
}) => {
const { t } = useTranslation();
//TODO Add
return (
<Row type="flex" justify="space-around">
<Col span={16}>
<Row type="flex" justify="space-around" align="middle">
{logo ? (
<Col span={4}>
<img alt="Shop Logo" src={logo} style={{ height: "40px" }} />
</Col>
) : null}
<Col span={14}>
<Menu
theme="dark"
className="header"
selectedKeys={selectedNavItem}
mode="horizontal"
onClick={handleMenuClick}
>
<Menu.Item>
<GlobalSearch />
</Menu.Item>
<Menu.Item key="home">
<Link to="/manage">
<Icon type="home" />
@@ -49,16 +57,62 @@ export default ({ landingHeader, selectedNavItem }) => {
</Menu.Item>
</Menu.SubMenu>
{!landingHeader ? null : (
<Menu.Item>
<ManageSignInButton />
<Menu.SubMenu title={t("menus.header.customers")}>
<Menu.Item key="owners">
<Link to="/manage/owners">
<Icon type="team" />
{t("menus.header.owners")}
</Link>
</Menu.Item>
)}
<Menu.Item key="vehicles">
<Link to="/manage/vehicles">
<Icon type="car" />
{t("menus.header.vehicles")}
</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
title={
<div>
<Avatar
size="medium"
alt="Avatar"
src={currentUser.photoURL ? currentUser.photoURL : UserImage}
style={{ margin: "10px" }}
/>
{currentUser.displayName || t("general.labels.unknown")}
</div>
}
>
<Menu.Item onClick={signOutStart()}>
{t("user.actions.signout")}
</Menu.Item>
<Menu.Item>
<Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
</Menu.Item>
<Menu.SubMenu
title={
<span>
<Icon type="global" />
<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">
{t("general.languages.french")}
</Menu.Item>
<Menu.Item actiontype="lang-select" key="es">
{t("general.languages.spanish")}
</Menu.Item>
</Menu.SubMenu>
</Menu.SubMenu>
</Menu>
</Col>
<Col span={6} offset={2}>
{!landingHeader ? <CurrentUserDropdown /> : null}
</Col>
<Col span={4}>{!landingHeader ? null : <ManageSignInButton />}</Col>
</Row>
);
};

View File

@@ -1,8 +1,52 @@
import React from "react";
import HeaderComponent from "./header.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import i18next from "i18next";
import { setUserLanguage, signOutStart } from "../../redux/user/user.actions";
import {
selectCurrentUser,
selectBodyshop
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop
});
const mapDispatchToProps = dispatch => ({
signOutStart: () => dispatch(signOutStart()),
setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(function HeaderContainer({
landingHeader,
currentUser,
bodyshop,
signOutStart,
setUserLanguage
}) {
const handleMenuClick = e => {
if (e.item.props.actiontype === "lang-select") {
i18next.changeLanguage(e.key, (err, t) => {
if (err)
return console.log("Error encountered when changing languages.", err);
setUserLanguage(e.key);
});
}
};
export default ({ landingHeader, signedIn }) => {
return (
<HeaderComponent landingHeader={landingHeader} selectedNavItem={null} />
<HeaderComponent
handleMenuClick={handleMenuClick}
signOutStart={signOutStart}
landingHeader={landingHeader}
selectedNavItem={null}
currentUser={currentUser}
logo={bodyshop ? bodyshop.logo_img_path : null}
/>
);
};
});

View File

@@ -10,45 +10,45 @@ export default function JobDetailCardsInsuranceComponent({ loading, data }) {
<CardTemplate loading={loading} title={t("jobs.labels.cards.insurance")}>
{data ? (
<span>
<div>{data?.ins_co_nm || t("general.labels.unknown")}</div>
<div>{data?.clm_no || t("general.labels.unknown")}</div>
<div>{data.ins_co_nm || t("general.labels.unknown")}</div>
<div>{data.clm_no || t("general.labels.unknown")}</div>
<div>
{t("jobs.labels.cards.filehandler")}
{data?.ins_ea ? (
{data.ins_ea ? (
<a href={`mailto:${data.ins_ea}`}>
<div>{`${data?.ins_ct_fn || ""} ${data?.ins_ct_ln || ""}`}</div>
<div>{`${data.ins_ct_fn || ""} ${data.ins_ct_ln || ""}`}</div>
</a>
) : (
<div>{`${data?.ins_ct_fn || ""} ${data?.ins_ct_ln || ""}`}</div>
<div>{`${data.ins_ct_fn || ""} ${data.ins_ct_ln || ""}`}</div>
)}
{data?.ins_ph1 ? (
<PhoneFormatter>{data?.ins_ph1}</PhoneFormatter>
{data.ins_ph1 ? (
<PhoneFormatter>{data.ins_ph1}</PhoneFormatter>
) : null}
</div>
<div>
{t("jobs.labels.cards.appraiser")}
{data?.est_ea ? (
{data.est_ea ? (
<a href={`mailto:${data.est_ea}`}>
<div>{`${data?.ins_ct_fn || ""} ${data?.ins_ct_ln || ""}`}</div>
<div>{`${data.ins_ct_fn || ""} ${data.ins_ct_ln || ""}`}</div>
</a>
) : (
<div>{`${data?.ins_ct_fn || ""} ${data?.ins_ct_ln || ""}`}</div>
<div>{`${data.ins_ct_fn || ""} ${data.ins_ct_ln || ""}`}</div>
)}
</div>
<div>
{t("jobs.labels.cards.estimator")}
{data?.est_ea ? (
{data.est_ea ? (
<a href={`mailto:${data.est_ea}`}>
<div>{`${data?.est_ct_fn || ""} ${data?.est_ct_ln || ""}`}</div>
<div>{`${data.est_ct_fn || ""} ${data.est_ct_ln || ""}`}</div>
</a>
) : (
<div>{`${data?.est_ct_fn || ""} ${data?.est_ct_ln || ""}`}</div>
<div>{`${data.est_ct_fn || ""} ${data.est_ct_ln || ""}`}</div>
)}
{data?.est_ph1 ? (
<PhoneFormatter>{data?.est_ph1}</PhoneFormatter>
) : null}
{data.est_ph1 ? (
<PhoneFormatter>{data.est_ph1}</PhoneFormatter>
) : null}
</div>
</span>
) : null}

View File

@@ -13,7 +13,7 @@ export default function JobDetailCardsNotesComponent({ loading, data }) {
const { t } = useTranslation();
return (
<CardTemplate
<CardTemplate
loading={loading}
title={t("jobs.labels.cards.notes")}
extraLink={`/manage/jobs/${data.id}#notes`}>
@@ -22,7 +22,7 @@ export default function JobDetailCardsNotesComponent({ loading, data }) {
<List
size='small'
bordered
dataSource={data?.notes}
dataSource={data.notes}
renderItem={item => (
<List.Item>
{item.critical ? (

View File

@@ -9,7 +9,7 @@ export default function JobDetailCardsVehicleComponent({ loading, data }) {
<CardTemplate
loading={loading}
title={t("jobs.labels.cards.vehicle")}
extraLink={data?.vehicle ? `/manage/vehicles/${data?.vehicle?.id}` : null}
extraLink={data.vehicle ? `/manage/vehicles/${data.vehicle.id}` : null}
>
{data ? (
<span>

View File

@@ -52,24 +52,28 @@ export default function JobLinesContainer({ jobId, form }) {
? searchText
? data.joblines.filter(
jl =>
jl.unq_seq
?.toString()
(jl.unq_seq || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
jl.line_desc
?.toLowerCase()
(jl.line_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
jl.part_type
?.toLowerCase()
(jl.part_type || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
jl.oem_partno
?.toLowerCase()
(jl.oem_partno || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
jl.op_code_desc
?.toLowerCase()
(jl.op_code_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
jl.db_price?.toString().includes(searchText.toLowerCase()) ||
jl.act_price?.toString().includes(searchText.toLowerCase())
(jl.db_price || "")
.toString()
.includes(searchText.toLowerCase()) ||
(jl.act_price || "")
.toString()
.includes(searchText.toLowerCase())
)
: data.joblines
: null

View File

@@ -41,9 +41,7 @@ export default connect(
jobId={jobId}
currentUser={currentUser}
shopId={
shopData.data?.bodyshops[0]?.id
? shopData.data?.bodyshops[0]?.id
: "error"
shopData.data.bodyshops[0].id ? shopData.data.bodyshops[0].id : "error"
}
/>
);

View File

@@ -106,7 +106,7 @@ export default withRouter(function JobsList({
ellipsis: true,
render: (text, record) => {
return record.vehicle ? (
<Link to={"manage/vehicles/" + record.vehicle.id}>
<Link to={"/manage/vehicles/" + record.vehicle.id}>
{record.vehicle.v_model_yr} {record.vehicle.v_make_desc}{" "}
{record.vehicle.v_model_desc}
</Link>
@@ -121,11 +121,11 @@ export default withRouter(function JobsList({
key: "plate_no",
width: "8%",
ellipsis: true,
sorter: (a, b) => alphaSort(a.vehicle?.plate_no, b.vehicle?.plate_no),
sorter: (a, b) => alphaSort(a.vehicle.plate_no, b.vehicle.plate_no),
sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
render: (text, record) => {
return record.vehicle?.plate_no ? (
return record.vehicle.plate_no ? (
<span>{record.vehicle.plate_no}</span>
) : (
t("general.labels.unknown")

View File

@@ -8,7 +8,11 @@ export default function LoadingSpinner({ loading = true, message, ...props }) {
spinning={loading}
className="loading-spinner"
size="large"
//delay="500"
style={{
position: "relative",
alignContent: "center"
}}
delay={200}
tip={message ? message : null}
>
{props.children}

View File

@@ -1,3 +1,2 @@
.loading-spinner {
text-align: center;
}

View File

@@ -14,7 +14,7 @@ export default function NoteUpsertModalComponent({
return (
<Modal
title={noteState?.id ? t("notes.actions.edit") : t("notes.actions.new")}
title={noteState.id ? t("notes.actions.edit") : t("notes.actions.new")}
visible={visible}
okText={t("general.labels.save")}
onOk={() => {
@@ -22,7 +22,8 @@ export default function NoteUpsertModalComponent({
}}
onCancel={() => {
changeVisibility(false);
}}>
}}
>
<div>
{t("notes.fields.critical")}
<Switch

View File

@@ -0,0 +1,106 @@
import { Button, Col, Form, Input, Row, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
import FormItemPhone from "../form-items-formatted/phone-form-item.component";
import ResetForm from "../form-items-formatted/reset-form-item.component";
export default function OwnerDetailFormComponent({ form, owner }) {
const { t } = useTranslation();
const {
isFieldsTouched,
resetFields,
getFieldDecorator,
getFieldValue
} = form;
return (
<div>
{isFieldsTouched() ? <ResetForm resetFields={resetFields} /> : null}
<Button type="primary" key="submit" htmlType="submit">
{t("general.labels.save")}
</Button>
<Row>
<Col span={8}>
<Form.Item label={t("owners.fields.ownr_ln")}>
{getFieldDecorator("ownr_ln", {
initialValue: owner.ownr_ln
})(<Input name="ownr_ln" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_fn")}>
{getFieldDecorator("ownr_fn", {
initialValue: owner.ownr_fn
})(<Input name="ownr_fn" />)}
</Form.Item>
<Form.Item label={t("owners.fields.allow_text_message")}>
{getFieldDecorator("allow_text_message", {
initialValue: owner.allow_text_message,
valuePropName: "checked"
})(<Switch name="allow_text_message" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_addr1")}>
{getFieldDecorator("ownr_addr1", {
initialValue: owner.ownr_addr1
})(<Input name="ownr_addr1" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_addr2")}>
{getFieldDecorator("ownr_addr2", {
initialValue: owner.ownr_addr2
})(<Input name="ownr_addr2" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_city")}>
{getFieldDecorator("ownr_city", {
initialValue: owner.ownr_city
})(<Input name="ownr_city" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_ctry")}>
{getFieldDecorator("ownr_ctry", {
initialValue: owner.ownr_ctry
})(<Input name="ownr_ctry" />)}
</Form.Item>
</Col>
<Col span={8}>
{" "}
<Form.Item label={t("owners.fields.ownr_ea")}>
{getFieldDecorator("ownr_ea", {
initialValue: owner.ownr_ea,
rules: [
{
type: "email",
message: "This is not a valid email address."
}
]
})(
<FormItemEmail name="ownr_ea" email={getFieldValue("ownr_ea")} />
)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_ph1")}>
{getFieldDecorator("ownr_ph1", {
initialValue: owner.ownr_ph1
})(<FormItemPhone customInput={Input} name="ownr_ph1" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_st")}>
{getFieldDecorator("ownr_st", {
initialValue: owner.ownr_st
})(<Input name="ownr_st" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_zip")}>
{getFieldDecorator("ownr_zip", {
initialValue: owner.ownr_zip
})(<Input name="ownr_zip" />)}
</Form.Item>
<Form.Item label={t("owners.fields.preferred_contact")}>
{getFieldDecorator("preferred_contact", {
initialValue: owner.preferred_contact
})(<Input name="preferred_contact" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_title")}>
{getFieldDecorator("ownr_title", {
initialValue: owner.ownr_title
})(<Input name="ownr_title" />)}
</Form.Item>
</Col>
</Row>
</div>
);
}

View File

@@ -0,0 +1,46 @@
import { Form, notification } from "antd";
import React from "react";
import { useMutation } from "react-apollo";
import { useTranslation } from "react-i18next";
import { UPDATE_OWNER } from "../../graphql/owners.queries";
import OwnerDetailFormComponent from "./owner-detail-form.component";
function OwnerDetailFormContainer({ form, owner, refetch }) {
const { t } = useTranslation();
const [updateOwner] = useMutation(UPDATE_OWNER);
const handleSubmit = e => {
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (err) {
notification["error"]({
message: t("owners.errors.validationtitle"),
description: t("owners.errors.validation")
});
}
if (!err) {
updateOwner({
variables: { ownerId: owner.id, owner: values }
}).then(r => {
notification["success"]({
message: t("owners.successes.save")
});
//TODO: Better way to reset the field decorators?
if (refetch) refetch().then();
form.resetFields();
});
}
});
};
return (
<Form onSubmit={handleSubmit} autoComplete="off">
<OwnerDetailFormComponent form={form} owner={owner} />
</Form>
);
}
export default Form.create({ name: "OwnerDetailFormContainer" })(
OwnerDetailFormContainer
);

View File

@@ -0,0 +1,59 @@
import React from "react";
import { Table } from "antd";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
export default function OwnerDetailJobsComponent({ owner }) {
const { t } = useTranslation();
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
ellipsis: true,
render: (text, record) => (
<Link to={`/manage/jobs/${record.id}`}>
{record.ro_number ? record.ro_number : `EST ${record.est_number}`}
</Link>
)
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "owner",
key: "owner",
render: (text, record) => (
<Link to={`/manage/vehicles/${record.vehicle.id}`}>
{`${record.vehicle.v_model_yr} ${record.vehicle.v_make_desc} ${record.vehicle.v_model_desc}`}
</Link>
)
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no"
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status"
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
)
}
];
return (
<Table
pagination={{ position: "bottom" }}
columns={columns.map(item => ({ ...item }))}
rowKey="id"
dataSource={owner.jobs}
/>
);
}

View File

@@ -37,7 +37,10 @@ export default connect(
}
if (!err) {
console.log("values", values);
updateUserDetails({ displayName: values.displayname });
updateUserDetails({
displayName: values.displayname,
photoURL: values.photoURL
});
}
});
};
@@ -59,6 +62,12 @@ export default connect(
rules: [{ required: true }]
})(<Input name="displayname" />)}
</Form.Item>
<Form.Item label={t("user.fields.photourl")}>
{getFieldDecorator("photoURL", {
initialValue: currentUser.photoURL
})(<Input name="photoURL" />)}
</Form.Item>
<Button
type="primary"
key="submit"

View File

@@ -11,6 +11,7 @@ export default function ScheduleDayViewComponent({ data, day }) {
<ScheduleCalendarWrapperComponent
events={data}
defaultView="day"
views={["day"]}
style={{ height: "40vh" }}
defaultDate={new Date(day)}
//onNavigate={e => console.log("e", e)}

View File

@@ -27,23 +27,24 @@ export default connect(
bodyshop,
refetch
}) {
const existingAppointments = useQuery(QUERY_APPOINTMENTS_BY_JOBID, {
variables: { jobid: jobId },
fetchPolicy: "network-only",
skip: !jobId
});
const [scheduleModalVisible, setscheduleModalVisible] = scheduleModalState;
const [appData, setAppData] = useState({ jobid: jobId, start: null });
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS, {
variables: {
jobId: jobId,
status: bodyshop.md_ro_statuses.default_scheduled
status: bodyshop.md_ro_statuses.default_scheduled
}
});
const [formData, setFormData] = useState({ notifyCustomer: false });
const { t } = useTranslation();
const existingAppointments = useQuery(QUERY_APPOINTMENTS_BY_JOBID, {
variables: { jobid: jobId },
fetchPolicy: "network-only",
skip: !scheduleModalVisible
});
return (
<ScheduleJobModalComponent
existingAppointments={existingAppointments}

View File

@@ -1,8 +0,0 @@
import React from "react";
import { useTranslation } from "react-i18next";
export default function SignoutComponent({ signOutStart }) {
const { t } = useTranslation();
return <div onClick={signOutStart}>{t("user.actions.signout")}</div>;
}

View File

@@ -0,0 +1,129 @@
import { Button, DatePicker, Form, Input, Row, Col } from "antd";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import ResetForm from "../form-items-formatted/reset-form-item.component";
export default function VehicleDetailFormComponent({ vehicle, form }) {
const { t } = useTranslation();
const { isFieldsTouched, resetFields, getFieldDecorator } = form;
return (
<div>
{isFieldsTouched() ? <ResetForm resetFields={resetFields} /> : null}
<Button type="primary" key="submit" htmlType="submit">
{t("general.labels.save")}
</Button>
<Row>
<Col span={8}>
<Form.Item label={t("vehicles.fields.v_vin")}>
{getFieldDecorator("v_vin", {
initialValue: vehicle.v_vin
})(<Input name="v_vin" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.plate_no")}>
{getFieldDecorator("plate_no", {
initialValue: vehicle.plate_no
})(<Input name="plate_no" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.plate_st")}>
{getFieldDecorator("plate_st", {
initialValue: vehicle.plate_st
})(<Input name="plate_st" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_type")}>
{getFieldDecorator("v_type", {
initialValue: vehicle.v_type
})(<Input name="v_type" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_trimcode")}>
{getFieldDecorator("v_trimcode", {
initialValue: vehicle.v_trimcode
})(<Input name="v_trimcode" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_tone")}>
{getFieldDecorator("v_tone", {
initialValue: vehicle.v_tone
})(<Input name="v_tone" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_bstyle")}>
{getFieldDecorator("v_bstyle", {
initialValue: vehicle.v_bstyle
})(<Input name="v_bstyle" />)}
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label={t("vehicles.fields.v_stage")}>
{getFieldDecorator("v_stage", {
initialValue: vehicle.v_stage
})(<Input name="v_stage" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_prod_dt")}>
{getFieldDecorator("v_prod_dt", {
initialValue: vehicle.v_prod_dt ? moment(vehicle.v_prod_dt) : null
})(<DatePicker name="v_prod_dt" />)}
</Form.Item>
{
//TODO Add handling for paint code json
}
<Form.Item label={t("vehicles.fields.v_paint_codes")}>
{getFieldDecorator("v_paint_codes", {
initialValue: JSON.stringify(vehicle.v_paint_codes)
})(<Input name="v_paint_codes" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_options")}>
{getFieldDecorator("v_options", {
initialValue: vehicle.v_options
})(<Input name="v_options" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_model_yr")}>
{getFieldDecorator("v_model_yr", {
initialValue: vehicle.v_model_yr
})(<Input name="v_model_yr" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_model_desc")}>
{getFieldDecorator("v_model_desc", {
initialValue: vehicle.v_model_desc
})(<Input name="v_model_desc" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.trim_color")}>
{getFieldDecorator("trim_color", {
initialValue: vehicle.trim_color
})(<Input name="trim_color" />)}
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label={t("vehicles.fields.v_mldgcode")}>
{getFieldDecorator("v_mldgcode", {
initialValue: vehicle.v_mldgcode
})(<Input name="v_mldgcode" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_makecode")}>
{getFieldDecorator("v_makecode", {
initialValue: vehicle.v_makecode
})(<Input name="v_makecode" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_make_desc")}>
{getFieldDecorator("v_make_desc", {
initialValue: vehicle.v_make_desc
})(<Input name="v_make_desc" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_engine")}>
{getFieldDecorator("v_engine", {
initialValue: vehicle.v_engine
})(<Input name="v_engine" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_cond")}>
{getFieldDecorator("v_cond", {
initialValue: vehicle.v_cond
})(<Input name="v_cond" />)}
</Form.Item>
<Form.Item label={t("vehicles.fields.v_color")}>
{getFieldDecorator("v_color", {
initialValue: vehicle.v_color
})(<Input name="v_color" />)}
</Form.Item>
</Col>
</Row>
</div>
);
}

View File

@@ -0,0 +1,46 @@
import React from "react";
import { Form, notification } from "antd";
import { useMutation } from "react-apollo";
import VehicleDetailFormComponent from "./vehicle-detail-form.component";
import { useTranslation } from "react-i18next";
import { UPDATE_VEHICLE } from "../../graphql/vehicles.queries";
function VehicleDetailFormContainer({ form, vehicle, refetch }) {
const { t } = useTranslation();
const [updateVehicle] = useMutation(UPDATE_VEHICLE);
const handleSubmit = e => {
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (err) {
notification["error"]({
message: t("vehicles.errors.validationtitle"),
description: t("vehicles.errors.validation")
});
}
if (!err) {
updateVehicle({
variables: { vehId: vehicle.id, vehicle: values }
}).then(r => {
notification["success"]({
message: t("vehicles.successes.save")
});
//TODO: Better way to reset the field decorators?
if (refetch) refetch().then();
form.resetFields();
});
}
});
};
return (
<Form onSubmit={handleSubmit} autoComplete="off">
<VehicleDetailFormComponent form={form} vehicle={vehicle} />
</Form>
);
}
export default Form.create({ name: "VehicleDetailFormContainer" })(
VehicleDetailFormContainer
);

View File

@@ -0,0 +1,59 @@
import React from "react";
import { Table } from "antd";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
export default function VehicleDetailJobsComponent({ vehicle }) {
const { t } = useTranslation();
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
ellipsis: true,
render: (text, record) => (
<Link to={`/manage/jobs/${record.id}`}>
{record.ro_number ? record.ro_number : `EST ${record.est_number}`}
</Link>
)
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
render: (text, record) => (
<Link to={`/manage/owners/${record.owner.id}`}>
{`${record.ownr_fn} ${record.ownr_ln}`}
</Link>
)
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no"
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status"
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
)
}
];
return (
<Table
pagination={{ position: "bottom" }}
columns={columns.map(item => ({ ...item }))}
rowKey="id"
dataSource={vehicle.jobs}
/>
);
}

View File

@@ -1,45 +0,0 @@
import React, { useState } from "react";
export default WithInlineEdit = WrappedComponent => props => {
const [editing, setEditing] = useState(false);
const [modified, setModified] = useState(false);
const [originalValue, setOriginalValue] = useState(null);
const toggleEdit = () => {
setEditing(!editing);
if (editing) {
this.input.focus();
}
};
return editing ? (
<Form.Item style={{ margin: 0 }}>
{form.getFieldDecorator(dataIndex, {
rules: [
{
required: true,
message: `${title} is required.`
}
],
initialValue: record[dataIndex]
})(
<Input
ref={node => (this.input = node)}
onPressEnter={this.save}
onBlur={this.save}
/>
)}
</Form.Item>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }}
onClick={toggleEdit}
>
{children}
</div>
);
};
export default WithInlineEdit;

View File

@@ -19,7 +19,7 @@ const errorLink = onError(
}
}
if (networkError) {
if (networkError?.message.includes("JWTExpired")) {
if (networkError.message.includes("JWTExpired")) {
expired = true;
}
}

View File

@@ -18,3 +18,50 @@ export const QUERY_SEARCH_OWNER_BY_IDX = gql`
}
}
`;
export const QUERY_OWNER_BY_ID = gql`
query QUERY_OWNER_BY_ID($id: uuid!) {
owners_by_pk(id: $id) {
id
allow_text_message
ownr_addr1
ownr_addr2
ownr_co_nm
ownr_city
ownr_ctry
ownr_ea
ownr_fn
ownr_ph1
ownr_ln
ownr_ph2
ownr_st
ownr_title
ownr_zip
preferred_contact
jobs {
id
ro_number
est_number
clm_no
status
clm_total
vehicle {
id
v_model_yr
v_model_desc
v_make_desc
}
}
}
}
`;
export const UPDATE_OWNER = gql`
mutation UPDATE_OWNER($ownerId: uuid!, $owner: owners_set_input!) {
update_owners(where: { id: { _eq: $ownerId } }, _set: $owner) {
returning {
id
}
}
}
`;

View File

@@ -0,0 +1,55 @@
import { gql } from "apollo-boost";
export const QUERY_VEHICLE_BY_ID = gql`
query QUERY_VEHICLE_BY_ID($id: uuid!) {
vehicles_by_pk(id: $id) {
created_at
db_v_code
id
plate_no
plate_st
v_vin
v_type
v_trimcode
v_tone
v_stage
v_prod_dt
v_paint_codes
v_options
v_model_yr
v_model_desc
v_mldgcode
v_makecode
v_make_desc
v_engine
v_cond
v_color
v_bstyle
updated_at
trim_color
jobs {
id
ro_number
ownr_fn
est_number
ownr_ln
owner {
id
}
clm_no
status
clm_total
}
}
}
`;
export const UPDATE_VEHICLE = gql`
mutation UPDATE_VEHICLE($vehId: uuid!, $vehicle: vehicles_set_input!) {
update_vehicles(where: { id: { _eq: $vehId } }, _set: $vehicle) {
returning {
id
}
}
}
`;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -1,22 +1,18 @@
import { Alert, Button, Form, Icon, Tabs } from "antd";
import { Form, Icon, Tabs } from "antd";
import React, { useContext } from "react";
import { useTranslation } from "react-i18next";
import {
FaHardHat,
FaInfo,
FaRegStickyNote,
FaShieldAlt
} from "react-icons/fa";
import { FaHardHat, FaInfo, FaRegStickyNote, FaShieldAlt } from "react-icons/fa";
import ResetForm from "../../components/form-items-formatted/reset-form-item.component";
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
import JobsDetailClaims from "../../components/jobs-detail-claims/jobs-detail-claims.component";
import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
import JobsDetailFinancials from "../../components/jobs-detail-financial/jobs-detail-financial.component";
import JobsDetailHeader from "../../components/jobs-detail-header/jobs-detail-header.component";
import JobsDetailInsurance from "../../components/jobs-detail-insurance/jobs-detail-insurance.component";
import JobsDocumentsContainer from "../../components/jobs-documents/jobs-documents.container";
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
import JobDetailFormContext from "./jobs-detail.page.context";
import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
import JobDetailFormContext from "./jobs-detail.page.context";
export default function JobsDetailPage({
job,
@@ -60,19 +56,7 @@ export default function JobsDetailPage({
updateJobStatus={updateJobStatus}
/>
{isFieldsTouched() ? (
<Alert
message={
<div>
{t("general.messages.unsavedchanges")}
<Button onClick={() => resetFields()}>
{t("general.actions.reset")}
</Button>
</div>
}
closable
/>
) : null}
{isFieldsTouched() ? <ResetForm resetFields={resetFields} /> : null}
<Tabs defaultActiveKey="claimdetail">
<Tabs.TabPane

View File

@@ -1,8 +1,11 @@
import React from 'react'
import ManageRootPageComponent from './manage-root.page.component'
import React, { useEffect } from "react";
import ManageRootPageComponent from "./manage-root.page.component";
import { useTranslation } from "react-i18next";
export default function ManageRootPageContainer() {
return (
<ManageRootPageComponent />
)
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.manageroot");
}, [t]);
return <ManageRootPageComponent />;
}

View File

@@ -1,6 +1,34 @@
import React from "react";
import React, { useEffect } from "react";
import ManagePage from "./manage.page";
import { useQuery } from "react-apollo";
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
import { setBodyshop } from "../../redux/user/user.actions";
import { connect } from "react-redux";
import { notification } from "antd";
import { useTranslation } from "react-i18next";
export default function ManagePageContainer() {
return <ManagePage />;
}
const mapDispatchToProps = dispatch => ({
setBodyshop: bs => dispatch(setBodyshop(bs))
});
export default connect(
null,
mapDispatchToProps
)(function ManagePageContainer({ match, setBodyshop }) {
const { error, data } = useQuery(QUERY_BODYSHOP, {
fetchPolicy: "network-only"
});
const { t } = useTranslation();
if (error) {
notification["error"]({ message: t("bodyshop.errors.loading") });
}
useEffect(() => {
if (data) setBodyshop(data.bodyshops[0]);
return () => {
//
};
}, [data, setBodyshop]);
return <ManagePage match={match} />;
});

View File

@@ -1,19 +1,12 @@
import { BackTop, Layout, notification } from "antd";
import { BackTop, Layout } from "antd";
import React, { lazy, Suspense, useEffect } from "react";
import { useTranslation } from "react-i18next";
//This page will handle all routing for the entire application.
import { connect } from "react-redux";
import { Route } from "react-router";
import { createStructuredSelector } from "reselect";
import { useQuery } from "react-apollo";
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
import FooterComponent from "../../components/footer/footer.component";
//Component Imports
import HeaderContainer from "../../components/header/header.container";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
//const WhiteBoardPage = lazy(() => import("../white-board/white-board.page"));
import { setBodyshop } from "../../redux/user/user.actions";
import "./manage.page.styles.scss";
const ManageRootPage = lazy(() =>
@@ -36,29 +29,21 @@ const ChatWindowContainer = lazy(() =>
const ScheduleContainer = lazy(() =>
import("../schedule/schedule.page.container")
);
const VehiclesContainer = lazy(() =>
import("../vehicles/vehicles.page.container")
);
const VehiclesDetailContainer = lazy(() =>
import("../vehicles-detail/vehicles-detail.page.container")
);
const OwnersContainer = lazy(() => import("../owners/owners.page.container"));
const OwnersDetailContainer = lazy(() =>
import("../owners-detail/owners-detail.page.container")
);
const { Header, Content, Footer } = Layout;
const mapDispatchToProps = dispatch => ({
setBodyshop: bs => dispatch(setBodyshop(bs))
});
export default connect(
null,
mapDispatchToProps
)(function Manage({ match, setBodyshop }) {
export default function Manage({ match }) {
const { t } = useTranslation();
const { error, data } = useQuery(QUERY_BODYSHOP, {
fetchPolicy: "network-only"
});
if (error) {
notification["error"]({ message: t("bodyshop.errors.loading") });
}
if (data) {
setBodyshop(data.bodyshops[0]);
}
useEffect(() => {
document.title = t("titles.app");
}, [t]);
@@ -97,7 +82,26 @@ export default connect(
path={`${match.path}/profile`}
component={ProfilePage}
/>
<Route
exact
path={`${match.path}/vehicles`}
component={VehiclesContainer}
/>
<Route
exact
path={`${match.path}/vehicles/:vehId`}
component={VehiclesDetailContainer}
/>
<Route
exact
path={`${match.path}/owners`}
component={OwnersContainer}
/>
<Route
exact
path={`${match.path}/owners/:ownerId`}
component={OwnersDetailContainer}
/>
<Route
exact
path={`${match.path}/schedule`}
@@ -119,4 +123,4 @@ export default connect(
<BackTop />
</Layout>
);
});
}

View File

@@ -0,0 +1,11 @@
import React from "react";
import OwnerDetailForm from "../../components/owner-detail-form/owner-detail-form.container";
import OwnerDetailJobsComponent from "../../components/owner-detail-jobs/owner-detail-jobs.component";
export default function OwnersDetailComponent({ owner, refetch }) {
return (
<div>
<OwnerDetailForm owner={owner} refetch={refetch} />
<OwnerDetailJobsComponent owner={owner} />
</div>
);
}

View File

@@ -0,0 +1,28 @@
import React from "react";
import OwnersDetailComponent from "./owners-detail.page.component";
import { useTranslation } from "react-i18next";
import { useQuery } from "react-apollo";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import AlertComponent from "../../components/alert/alert.component";
import { QUERY_OWNER_BY_ID } from "../../graphql/owners.queries";
export default function OwnersDetailContainer({ match }) {
const { ownerId } = match.params;
const { t } = useTranslation();
const { loading, data, error, refetch } = useQuery(QUERY_OWNER_BY_ID, {
variables: { id: ownerId },
fetchPolicy: "network-only"
});
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
if (data.owners_by_pk)
return (
<OwnersDetailComponent owner={data.owners_by_pk} refetch={refetch} />
);
else
return (
<AlertComponent message={t("owners.errors.noaccess")} type="error" />
);
}

View File

@@ -0,0 +1,9 @@
import React from 'react'
export default function OwnersPageComponent() {
return (
<div>
Owners Page
</div>
)
}

View File

@@ -0,0 +1,8 @@
import React from 'react'
import OwnersPageComponent from './owners.page.component'
export default function OwnersPageContainer() {
return (
<OwnersPageComponent />
)
}

View File

@@ -1,6 +1,11 @@
import React from "react";
import React, { useEffect } from "react";
import ProfilePage from "./profile.page";
import { useTranslation } from "react-i18next";
export default function ProfileContainerPage() {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.profile");
}, [t]);
return <ProfilePage />;
}

View File

@@ -1,16 +1,9 @@
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Layout } from "antd";
import ProfileSideBar from "../../components/profile-sidebar/profile-sidebar.component";
import React, { useState } from "react";
import ProfileContent from "../../components/profile-content/profile-content.component";
import ProfileSideBar from "../../components/profile-sidebar/profile-sidebar.component";
export default function ProfilePage() {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.profile");
}, [t]);
const [sidebarSelection, setSidebarSelection] = useState({ key: "profile" });
return (
<Layout>

View File

@@ -0,0 +1,12 @@
import React from "react";
import VehicleDetailFormContainer from "../../components/vehicle-detail-form/vehicle-detail-form.container";
import VehicleDetailJobsComponent from "../../components/vehicle-detail-jobs/vehicle-detail-jobs.component";
export default function VehicleDetailComponent({ vehicle, refetch }) {
return (
<div>
<VehicleDetailFormContainer vehicle={vehicle} refetch={refetch} />
<VehicleDetailJobsComponent vehicle={vehicle} />
</div>
);
}

View File

@@ -0,0 +1,37 @@
import React, { useEffect } from "react";
import VehicleDetailComponent from "./vehicles-detail.page.component";
import { useQuery } from "react-apollo";
import { QUERY_VEHICLE_BY_ID } from "../../graphql/vehicles.queries";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import AlertComponent from "../../components/alert/alert.component";
import { useTranslation } from "react-i18next";
export default function VehicleDetailContainer({ match }) {
const { vehId } = match.params;
const { t } = useTranslation();
const { loading, data, error, refetch } = useQuery(QUERY_VEHICLE_BY_ID, {
variables: { id: vehId },
fetchPolicy: "network-only"
});
useEffect(() => {
document.title = t("titles.vehicledetail", {
vehicle:
data && data.vehicles_by_pk
? `${data.vehicles_by_pk.v_model_yr} ${data.vehicles_by_pk.v_make_desc} ${data.vehicles_by_pk.v_model_desc}`
: ""
});
}, [t, data]);
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
if (data.vehicles_by_pk)
return (
<VehicleDetailComponent vehicle={data.vehicles_by_pk} refetch={refetch} />
);
else
return (
<AlertComponent message={t("vehicles.errors.noaccess")} type="error" />
);
}

View File

@@ -0,0 +1,9 @@
import React from 'react'
export default function VehiclesPageComponent() {
return (
<div>
Vehiculos
</div>
)
}

View File

@@ -0,0 +1,6 @@
import React from "react";
import VehiclesPageComponent from "./vehicles.page.component";
export default function VehiclesPageContainer() {
return <VehiclesPageComponent />;
}

View File

@@ -34,6 +34,7 @@ export function* signInWithEmail({ payload: { email, password } }) {
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
authorized: true
})
);
@@ -62,6 +63,7 @@ export function* isUserAuthenticated() {
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
authorized: true
})
);

View File

@@ -246,9 +246,12 @@
"header": {
"activejobs": "Active Jobs",
"availablejobs": "Available Jobs",
"customers": "Customers",
"home": "Home",
"jobs": "Jobs",
"schedule": "Schedule"
"owners": "Owners",
"schedule": "Schedule",
"vehicles": "Vehicles"
},
"jobsdetail": {
"claimdetail": "Claim Details",
@@ -288,16 +291,29 @@
}
},
"owners": {
"errors": {
"noaccess": "The record does not exist or you do not have access to it. "
},
"fields": {
"allow_text_message": "Permission to Text?",
"ownr_addr1": "Address",
"ownr_addr2": "Address 2",
"ownr_city": "City",
"ownr_ctry": "Country",
"ownr_ea": "Email",
"ownr_fn": "First Name",
"ownr_ln": "Last Name",
"ownr_ph1": "Phone 1"
"ownr_ph1": "Phone 1",
"ownr_st": "State/Province",
"ownr_title": "Title",
"ownr_zip": "Zip/Postal Code",
"preferred_contact": "Preferred Contact Method"
},
"labels": {
"existing_owners": "Existing Owners"
},
"successes": {
"save": "Owner saved successfully."
}
},
"profile": {
@@ -311,8 +327,10 @@
"jobsavailable": "Available Jobs | $t(titles.app)",
"jobsdetail": "Job {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Job Documents {{ro_number}} | $t(titles.app)",
"manageroot": "Home | $t(titles.app)",
"profile": "My Profile | $t(titles.app)",
"schedule": "Schedule | $t(titles.app)"
"schedule": "Schedule | $t(titles.app)",
"vehicledetail": "Vehicle Details {{vehicle}} | $t(titles.app)"
},
"user": {
"actions": {
@@ -320,12 +338,40 @@
"updateprofile": "Update Profile"
},
"fields": {
"displayname": "Display Name"
"displayname": "Display Name",
"photourl": "Avatar URL"
}
},
"vehicles": {
"errors": {
"noaccess": "The vehicle does not exist or you do not have access to it.",
"validation": "Please ensure all fields are entered correctly.",
"validationtitle": "Validation Error"
},
"fields": {
"plate_no": "License Plate"
"plate_no": "License Plate",
"plate_st": "Plate Jurisdiction",
"trim_color": "Trim Color",
"v_bstyle": "Body Style",
"v_color": "Color",
"v_cond": "Condition",
"v_engine": "Engine",
"v_make_desc": "Make",
"v_makecode": "Make Code",
"v_mldgcode": "Molding Code",
"v_model_desc": "Model",
"v_model_yr": "Year",
"v_options": "Options",
"v_paint_codes": "Paint Codes",
"v_prod_dt": "Production Date",
"v_stage": "Stage",
"v_tone": "Tone",
"v_trimcode": "Trim Code",
"v_type": "Type",
"v_vin": "Vehicle Identification Number"
},
"successes": {
"save": "Vehicle saved successfully."
}
}
}

View File

@@ -246,9 +246,12 @@
"header": {
"activejobs": "Empleos activos",
"availablejobs": "Trabajos disponibles",
"customers": "Clientes",
"home": "Casa",
"jobs": "Trabajos",
"schedule": "Programar"
"owners": "propietarios",
"schedule": "Programar",
"vehicles": "Vehículos"
},
"jobsdetail": {
"claimdetail": "Detalles de la reclamación",
@@ -288,16 +291,29 @@
}
},
"owners": {
"errors": {
"noaccess": "El registro no existe o no tiene acceso a él."
},
"fields": {
"allow_text_message": "Permiso de texto?",
"ownr_addr1": "Dirección",
"ownr_addr2": "Dirección 2",
"ownr_city": "ciudad",
"ownr_ctry": "País",
"ownr_ea": "Email",
"ownr_fn": "Nombre de pila",
"ownr_ln": "Apellido",
"ownr_ph1": ""
"ownr_ph1": "Teléfono 1",
"ownr_st": "Provincia del estado",
"ownr_title": "Título",
"ownr_zip": "código postal",
"preferred_contact": "Método de Contacto Preferido"
},
"labels": {
"existing_owners": "Propietarios existentes"
},
"successes": {
"save": "Propietario guardado con éxito."
}
},
"profile": {
@@ -311,8 +327,10 @@
"jobsavailable": "Empleos disponibles | $t(titles.app)",
"jobsdetail": "Trabajo {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $ t (títulos.app)",
"manageroot": "Casa | $t(titles.app)",
"profile": "Mi perfil | $t(titles.app)",
"schedule": "Horario | $t(titles.app)"
"schedule": "Horario | $t(titles.app)",
"vehicledetail": "Detalles del vehículo {{vehicle}} | $t(titles.app)"
},
"user": {
"actions": {
@@ -320,12 +338,40 @@
"updateprofile": "Actualización del perfil"
},
"fields": {
"displayname": "Nombre para mostrar"
"displayname": "Nombre para mostrar",
"photourl": "URL de avatar"
}
},
"vehicles": {
"errors": {
"noaccess": "El vehículo no existe o usted no tiene acceso a él.",
"validation": "Asegúrese de que todos los campos se ingresen correctamente.",
"validationtitle": "Error de validacion"
},
"fields": {
"plate_no": "Placa"
"plate_no": "Placa",
"plate_st": "Jurisdicción de placas",
"trim_color": "Recortar color",
"v_bstyle": "Tipo de cuerpo",
"v_color": "Color",
"v_cond": "condición",
"v_engine": "Motor",
"v_make_desc": "Hacer",
"v_makecode": "Hacer código",
"v_mldgcode": "Código de moldeo",
"v_model_desc": "Modelo",
"v_model_yr": "año",
"v_options": "Opciones",
"v_paint_codes": "Códigos de pintura",
"v_prod_dt": "Fecha de producción",
"v_stage": "Escenario",
"v_tone": "Tono",
"v_trimcode": "Código de recorte",
"v_type": "Tipo",
"v_vin": "Número de identificación del vehículo"
},
"successes": {
"save": "Vehículo guardado con éxito."
}
}
}

View File

@@ -246,9 +246,12 @@
"header": {
"activejobs": "Emplois actifs",
"availablejobs": "Emplois disponibles",
"customers": "Les clients",
"home": "Accueil",
"jobs": "Emplois",
"schedule": "Programme"
"owners": "Propriétaires",
"schedule": "Programme",
"vehicles": "Véhicules"
},
"jobsdetail": {
"claimdetail": "Détails de la réclamation",
@@ -288,16 +291,29 @@
}
},
"owners": {
"errors": {
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès."
},
"fields": {
"allow_text_message": "Autorisation de texte?",
"ownr_addr1": "Adresse",
"ownr_addr2": "Adresse 2 ",
"ownr_city": "Ville",
"ownr_ctry": "Pays",
"ownr_ea": "Email",
"ownr_fn": "Prénom",
"ownr_ln": "Nom de famille",
"ownr_ph1": ""
"ownr_ph1": "Téléphone 1",
"ownr_st": "Etat / Province",
"ownr_title": "Titre",
"ownr_zip": "Zip / code postal",
"preferred_contact": "Méthode de contact préférée"
},
"labels": {
"existing_owners": "Propriétaires existants"
},
"successes": {
"save": "Le propriétaire a bien enregistré."
}
},
"profile": {
@@ -311,8 +327,10 @@
"jobsavailable": "Emplois disponibles | $t(titles.app)",
"jobsdetail": "Travail {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documents de travail {{ro_number}} | $ t (titres.app)",
"manageroot": "Accueil | $t(titles.app)",
"profile": "Mon profil | $t(titles.app)",
"schedule": "Horaire | $t(titles.app)"
"schedule": "Horaire | $t(titles.app)",
"vehicledetail": "Détails du véhicule {{vehicle} | $t(titles.app)"
},
"user": {
"actions": {
@@ -320,12 +338,40 @@
"updateprofile": "Mettre à jour le profil"
},
"fields": {
"displayname": "Afficher un nom"
"displayname": "Afficher un nom",
"photourl": "URL de l'avatar"
}
},
"vehicles": {
"errors": {
"noaccess": "Le véhicule n'existe pas ou vous n'y avez pas accès.",
"validation": "Veuillez vous assurer que tous les champs sont correctement entrés.",
"validationtitle": "Erreur de validation"
},
"fields": {
"plate_no": "Plaque d'immatriculation"
"plate_no": "Plaque d'immatriculation",
"plate_st": "Juridiction de la plaque",
"trim_color": "Couleur de garniture",
"v_bstyle": "Style corporel",
"v_color": "Couleur",
"v_cond": "Etat",
"v_engine": "moteur",
"v_make_desc": "Faire",
"v_makecode": "Faire du code",
"v_mldgcode": "Code de moulage",
"v_model_desc": "Modèle",
"v_model_yr": "année",
"v_options": "Les options",
"v_paint_codes": "Codes de peinture",
"v_prod_dt": "Date de production",
"v_stage": "Étape",
"v_tone": "ton",
"v_trimcode": "Code de coupe",
"v_type": "Type",
"v_vin": "Plaque d'immatriculation"
},
"successes": {
"save": "Le véhicule a été enregistré avec succès."
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,14 +17,14 @@
},
"dependencies": {
"apollo-cache-persist": "^0.1.1",
"aws-sdk": "^2.603.0",
"aws-sdk": "^2.613.0",
"body-parser": "^1.18.3",
"compression": "^1.7.4",
"cors": "2.8.5",
"dotenv": "7.0.0",
"dotenv": "8.2.0",
"express": "^4.16.4",
"express-sslify": "^1.2.0",
"firebase-tools": "^7.9.0"
"firebase-tools": "^7.12.1"
},
"devDependencies": {
"concurrently": "^4.0.1",

View File

@@ -386,10 +386,10 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
aws-sdk@^2.603.0:
version "2.603.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.603.0.tgz#0920756d2666f4fcfa7233841ef35cd04da81348"
integrity sha512-+VlskUDLZLQDDlaVa0Tb02aEFEWcKkTfTew1SGYwce9hUrKcR33IX4e9kM6MyI7UeLQAl0v8dagTniP67UrTQw==
aws-sdk@^2.613.0:
version "2.613.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.613.0.tgz#f0d4c99f0df7ee8227d331dde19d5251b31a5da7"
integrity sha512-FYLaVtC/AlrcnjsPw1JhAsKd6yapr918Mk0jAcw3yFZp1sI2V0Um+2pmijLFsV+nNRxFlCVJRhhFWB5GK6yALA==
dependencies:
buffer "4.9.1"
events "1.1.1"
@@ -1092,10 +1092,10 @@ dot-prop@^4.1.0:
dependencies:
is-obj "^1.0.0"
dotenv@7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c"
integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==
dotenv@8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
dotenv@^6.1.0:
version "6.2.0"
@@ -1548,10 +1548,10 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
firebase-tools@^7.9.0:
version "7.9.0"
resolved "https://registry.yarnpkg.com/firebase-tools/-/firebase-tools-7.9.0.tgz#32661980943f5ebbb2d82c126762c468ae0cff9c"
integrity sha512-dYxuFflaPAkic2KQyPJ6spox8KzlQRb4Xyk6oVDorBV0YmBIJ3VQhJYbwSZwwDlbCmiPiMXNBM26JUifp09Qfw==
firebase-tools@^7.12.1:
version "7.12.1"
resolved "https://registry.yarnpkg.com/firebase-tools/-/firebase-tools-7.12.1.tgz#af78bbc446ae46d722938bd8009c351d52ec01f3"
integrity sha512-4cfHltsfUzKyt03OH9JUYrCPsq0OIZ9i54IzJRjJgs68NJDLrhUislBipw5Am0QgF8QDYpzz8wLTH56fNgmx8g==
dependencies:
"@google-cloud/pubsub" "^1.1.5"
JSONStream "^1.2.1"
@@ -1574,6 +1574,7 @@ firebase-tools@^7.9.0:
fs-extra "^0.23.1"
glob "^7.1.2"
google-auto-auth "^0.7.2"
google-gax "~1.12.0"
inquirer "~6.3.1"
jsonschema "^1.0.2"
jsonwebtoken "^8.2.1"
@@ -1587,7 +1588,7 @@ firebase-tools@^7.9.0:
portfinder "^1.0.23"
progress "^2.0.3"
request "^2.87.0"
semver "^5.0.3"
semver "^5.7.1"
superstatic "^6.0.1"
tar "^4.3.0"
tcp-port-used "^1.0.1"
@@ -1843,7 +1844,7 @@ google-auto-auth@^0.7.2:
google-auth-library "^0.10.0"
request "^2.79.0"
google-gax@^1.7.5:
google-gax@^1.7.5, google-gax@~1.12.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-1.12.0.tgz#f926f7e6abda245db38ecbebbbf58daaf3a8f687"
integrity sha512-BeeoxVO6y9K20gUsexUwptutd0PfrTItrA02JWwwstlBIOAcvgFp86MHWufQsnrkPVhxBjHXq65aIkSejtJjDg==
@@ -3508,7 +3509,7 @@ semver-diff@^2.0.0:
dependencies:
semver "^5.0.3"
"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.5.0, semver@^5.6.0:
"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==