diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index b4aaee9da..7c733bcdc 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -5754,6 +5754,27 @@ + + addtoproduction + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + changestatus false @@ -5906,6 +5927,27 @@ errors + + addingtoproduction + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + creating false @@ -8876,6 +8918,27 @@ successes + + addedtoproduction + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + all_deleted false diff --git a/client/src/App/App.container.jsx b/client/src/App/App.container.jsx index bcf9825c6..ab18d78e2 100644 --- a/client/src/App/App.container.jsx +++ b/client/src/App/App.container.jsx @@ -15,14 +15,14 @@ import { auth } from "../firebase/firebase.utils"; import errorLink from "../graphql/apollo-error-handling"; import App from "./App"; -import LogRocket from 'logrocket'; -LogRocket.init('gvfvfw/bodyshopapp'); +import LogRocket from "logrocket"; +LogRocket.init("gvfvfw/bodyshopapp"); export default class AppContainer extends Component { constructor() { super(); const httpLink = new HttpLink({ - uri: process.env.REACT_APP_GRAPHQL_ENDPOINT + uri: process.env.REACT_APP_GRAPHQL_ENDPOINT, }); const wsLink = new WebSocketLink({ @@ -36,18 +36,18 @@ export default class AppContainer extends Component { if (token) { return { headers: { - authorization: token ? `Bearer ${token}` : "" - } + authorization: token ? `Bearer ${token}` : "", + }, }; } - } - } + }, + }, }); const subscriptionMiddleware = { applyMiddleware: async (options, next) => { options.authToken = await auth.currentUser.getIdToken(true); next(); - } + }, }; wsLink.subscriptionClient.use([subscriptionMiddleware]); @@ -73,13 +73,13 @@ export default class AppContainer extends Component { ); const authLink = setContext((_, { headers }) => { - return auth.currentUser.getIdToken().then(token => { + return auth.currentUser.getIdToken().then((token) => { if (token) { return { headers: { ...headers, - authorization: token ? `Bearer ${token}` : "" - } + authorization: token ? `Bearer ${token}` : "", + }, }; } else { return { headers }; @@ -91,12 +91,12 @@ export default class AppContainer extends Component { delay: { initial: 300, max: 5, - jitter: true + jitter: true, }, attempts: { max: 5, - retryIf: (error, _operation) => !!error - } + retryIf: (error, _operation) => !!error, + }, }); const middlewares = []; @@ -110,7 +110,7 @@ export default class AppContainer extends Component { const client = new ApolloClient({ link: ApolloLink.from(middlewares), cache, - connectToDevTools: true + connectToDevTools: true, }); this.state = { client }; diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.addtoproduction.util.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.addtoproduction.util.jsx new file mode 100644 index 000000000..19c44ed83 --- /dev/null +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.addtoproduction.util.jsx @@ -0,0 +1,33 @@ +import { notification } from "antd"; +import i18n from "i18next"; +import { UPDATE_JOB } from "../../graphql/jobs.queries"; + +export default function AddToProduction( + apolloClient, + jobId, + completionCallback +) { + //get a list of all fields on the job + apolloClient + .mutate({ + mutation: UPDATE_JOB, + variables: { jobId: jobId, job: { inproduction: true } }, + }) + .then((res) => { + notification["success"]({ + message: i18n.t("jobs.successes.addedtoproduction"), + }); + if (completionCallback) completionCallback(); + }) + .catch((error) => { + notification["errors"]({ + message: i18n.t("jobs.errors.addingtoproduction", { + error: JSON.stringify(error), + }), + }); + }); + + //insert the new job. call the callback with the returned ID when done. + + return; +} diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx index 164c96c3c..afbfc84b2 100644 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx @@ -1,15 +1,15 @@ -import React from "react"; -import { Menu, Dropdown, Button, Popconfirm } from "antd"; -import { useTranslation } from "react-i18next"; import { DownCircleFilled } from "@ant-design/icons"; -import { Link } from "react-router-dom"; -import DuplicateJob from "./jobs-detail-header-actions.duplicate"; import { useApolloClient } from "@apollo/react-hooks"; - +import { Button, Dropdown, Menu, Popconfirm } from "antd"; +import React from "react"; +import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; +import { Link, useHistory } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; -import { useHistory } from "react-router-dom"; +import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util"; +import DuplicateJob from "./jobs-detail-header-actions.duplicate.util"; + const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser bodyshop: selectBodyshop, @@ -23,22 +23,27 @@ export function JobsDetailHeaderActions({ job, bodyshop }) { const client = useApolloClient(); const history = useHistory(); const statusmenu = ( - - + + + }}> {t("menus.jobsactions.newcccontract")} - + AddToProduction(client, job.id)}> + {t("jobs.actions.addtoproduction")} + + e.stopPropagation()} onConfirm={() => DuplicateJob( @@ -50,15 +55,14 @@ export function JobsDetailHeaderActions({ job, bodyshop }) { } ) } - getPopupContainer={(trigger) => trigger.parentNode} - > + getPopupContainer={(trigger) => trigger.parentNode}> {t("menus.jobsactions.duplicate")} ); return ( - + diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.duplicate.js b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util.js similarity index 100% rename from client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.duplicate.js rename to client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util.js diff --git a/client/src/components/production-board-kanban/production-board-kanban.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.component.jsx index 06507f545..f8281b8d2 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.component.jsx @@ -1,44 +1,41 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import Board from "react-trello"; import { Card } from "antd"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, +}); -export default function ProductionBoardKanbanComponent({ +export function ProductionBoardKanbanComponent({ loading, data, columnState, + bodyshop, }) { console.log("data", data); - const data2 = { - lanes: [ - { - id: "lane1", - title: "Planned Tasks", - label: "2/2", - cards: [ - { - id: "Card1", - title: "Write Blog", - description: "Can AI make memes", - label: "30 mins", - }, - { - id: "Card2", - title: "Pay Rent", - description: "Transfer via NEFT", - label: "5 mins", - metadata: { sha: "be312a1" }, - }, - ], - }, - { - id: "lane2", - title: "Completed", - label: "0/0", + + const [cards, setCards] = useState([]); + + useEffect(() => { + const cols = bodyshop.md_ro_statuses.open_statuses.map((s) => { + return { + id: s, + title: s, cards: [], - }, - ], - }; + }; + }); + if (data) + data.forEach((d) => + cols + .find((c) => c.id === d.status) + .cards.push({ id: d.id, title: d.est_number, description: d.ownr_fn }) + ); + + setCards(cols); + }, [data, bodyshop.md_ro_statuses.open_statuses, setCards]); const kanbanCard = (card) => {card.title}; @@ -46,7 +43,8 @@ export default function ProductionBoardKanbanComponent({ return (
- +
); } +export default connect(mapStateToProps, null)(ProductionBoardKanbanComponent); diff --git a/client/src/components/production-list-columns/production-list-columns.alert.component.jsx b/client/src/components/production-list-columns/production-list-columns.alert.component.jsx index d04892d74..ddd9012d8 100644 --- a/client/src/components/production-list-columns/production-list-columns.alert.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.alert.component.jsx @@ -17,7 +17,8 @@ export default function ProductionListColumnAlert({ record }) { job: { production_vars: { ...record.production_vars, - alert: !record.production_vars.alert, + alert: + (record.production_vars && !record.production_vars.alert) || true, }, }, }, diff --git a/client/src/components/production-list-columns/production-list-columns.data.js b/client/src/components/production-list-columns/production-list-columns.data.js index 397980211..1127f56e4 100644 --- a/client/src/components/production-list-columns/production-list-columns.data.js +++ b/client/src/components/production-list-columns/production-list-columns.data.js @@ -155,6 +155,7 @@ export default [ title: i18n.t("production.labels.alert"), dataIndex: "alert", key: "alert", + width: "4%", render: (text, record) => , }, { @@ -168,6 +169,7 @@ export default [ title: i18n.t("production.labels.cycletime"), dataIndex: "ct", key: "ct", + width: "3%", render: (text, record) => { let ct = 0; if (!!record.actual_in) { @@ -183,8 +185,10 @@ export default [ title: i18n.t("production.labels.bodypriority"), dataIndex: "bodypriority", key: "bodypriority", + width: "3%", sorter: (a, b) => - a.production_vars.bodypriority - b.production_vars.bodypriority, + ((a.production_vars && a.production_vars.bodypriority) || 11) - + ((b.production_vars && b.production_vars.bodypriority) || 11), render: (text, record) => ( ), @@ -193,8 +197,10 @@ export default [ title: i18n.t("production.labels.paintpriority"), dataIndex: "paintpriority", key: "paintpriority", + width: "3%", sorter: (a, b) => - a.production_vars.paintpriority - b.production_vars.paintpriority, + ((a.production_vars && a.production_vars.paintpriority) || 11) - + ((b.production_vars && b.production_vars.paintpriority) || 11), render: (text, record) => ( ), diff --git a/client/src/components/production-list-table/production-list-table.styles.scss b/client/src/components/production-list-table/production-list-table.styles.scss index 43bf298ff..9e260d7ef 100644 --- a/client/src/components/production-list-table/production-list-table.styles.scss +++ b/client/src/components/production-list-table/production-list-table.styles.scss @@ -1,5 +1,6 @@ .production-alert { animation: alertBlinker 1s linear infinite; + color: blue; } @keyframes alertBlinker { 50% { diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 8b1bad769..b52fc47d4 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -407,6 +407,7 @@ "actions": { "addDocuments": "Add Job Documents", "addNote": "Add Note", + "addtoproduction": "Add to Production", "changestatus": "Change Status", "convert": "Convert", "gotojob": "Go to Job", @@ -416,6 +417,7 @@ "schedule": "Schedule" }, "errors": { + "addingtoproduction": "Error adding to production. {{error}}", "creating": "Error encountered while creating job. {{error}}", "deleted": "Error deleting job.", "noaccess": "This job does not exist or you do not have access to it.", @@ -568,6 +570,7 @@ "vehicle_info": "Vehicle" }, "successes": { + "addedtoproduction": "Job added to production board.", "all_deleted": "{{count}} jobs deleted successfully.", "converted": "Job converted successfully.", "created": "Job created successfully. Click to view.", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index e0dca7d2d..ca6e0f7c0 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -407,6 +407,7 @@ "actions": { "addDocuments": "Agregar documentos de trabajo", "addNote": "Añadir la nota", + "addtoproduction": "", "changestatus": "Cambiar Estado", "convert": "Convertir", "gotojob": "", @@ -416,6 +417,7 @@ "schedule": "Programar" }, "errors": { + "addingtoproduction": "", "creating": "", "deleted": "Error al eliminar el trabajo.", "noaccess": "Este trabajo no existe o no tiene acceso a él.", @@ -568,6 +570,7 @@ "vehicle_info": "Vehículo" }, "successes": { + "addedtoproduction": "", "all_deleted": "{{count}} trabajos eliminados con éxito.", "converted": "Trabajo convertido con éxito.", "created": "Trabajo creado con éxito. Click para ver.", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 99229426e..883e96fdc 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -407,6 +407,7 @@ "actions": { "addDocuments": "Ajouter des documents de travail", "addNote": "Ajouter une note", + "addtoproduction": "", "changestatus": "Changer le statut", "convert": "Convertir", "gotojob": "", @@ -416,6 +417,7 @@ "schedule": "Programme" }, "errors": { + "addingtoproduction": "", "creating": "", "deleted": "Erreur lors de la suppression du travail.", "noaccess": "Ce travail n'existe pas ou vous n'y avez pas accès.", @@ -568,6 +570,7 @@ "vehicle_info": "Véhicule" }, "successes": { + "addedtoproduction": "", "all_deleted": "{{count}} travaux supprimés avec succès.", "converted": "Travail converti avec succès.", "created": "Le travail a été créé avec succès. Clique pour voir.",