diff --git a/client/src/components/courtesy-car-form/courtesy-car-form.component.jsx b/client/src/components/courtesy-car-form/courtesy-car-form.component.jsx index e9486afa9..f994f25dc 100644 --- a/client/src/components/courtesy-car-form/courtesy-car-form.component.jsx +++ b/client/src/components/courtesy-car-form/courtesy-car-form.component.jsx @@ -9,7 +9,7 @@ import { DateFormatter } from "../../utils/DateFormatter"; 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 FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; +//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component"; @@ -32,7 +32,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) { } /> - + {/* */} - {bodyshop.employees.map((emp) => ( - - {`${emp.first_name} ${emp.last_name}`} - - ))} + {bodyshop.employees + .filter((emp) => emp.active) + .map((emp) => ( + + {`${emp.first_name} ${emp.last_name}`} + + ))} diff --git a/client/src/components/job-lines-bill-reference/job-lines-bill-reference.component.jsx b/client/src/components/job-lines-bill-reference/job-lines-bill-reference.component.jsx index 84b43e9ab..7866d88fa 100644 --- a/client/src/components/job-lines-bill-reference/job-lines-bill-reference.component.jsx +++ b/client/src/components/job-lines-bill-reference/job-lines-bill-reference.component.jsx @@ -10,7 +10,7 @@ export default function JobLinesBillRefernece({ jobline }) { {subletRequired && } {`${(billLine.actual_price * billLine.quantity).toFixed(2)} (${ billLine.bill.vendor.name - })`} + } #${billLine.bill.invoice_number})`} ); } diff --git a/client/src/components/jobs-notes/jobs.notes.component.jsx b/client/src/components/jobs-notes/jobs.notes.component.jsx index 0d07e2516..928eec29c 100644 --- a/client/src/components/jobs-notes/jobs.notes.component.jsx +++ b/client/src/components/jobs-notes/jobs.notes.component.jsx @@ -124,7 +124,7 @@ export function JobNotesComponent({ messageObject={{ subject: Templates.individual_job_note.subject, }} - id={record.id} + id={jobId} /> ), diff --git a/client/src/components/parts-order-modal/parts-order-modal.component.jsx b/client/src/components/parts-order-modal/parts-order-modal.component.jsx index 073091cb0..7c206f59e 100644 --- a/client/src/components/parts-order-modal/parts-order-modal.component.jsx +++ b/client/src/components/parts-order-modal/parts-order-modal.component.jsx @@ -73,6 +73,7 @@ export function PartsOrderModalComponent({ options={vendorList} disabled={isReturn} preferredMake={preferredMake} + showPhone /> item.id === values.vendorid )[0]; + let vendorEmails = + matchingVendor && + matchingVendor.email && + matchingVendor.email.split(RegExp("[;,]")); + GenerateDocument( { name: isReturn @@ -206,7 +211,7 @@ export function PartsOrderModalContainer({ }, }, { - to: matchingVendor ? [matchingVendor.email] : null, + to: matchingVendor ? vendorEmails : null, replyTo: bodyshop.email, subject: isReturn ? Templates.parts_return_slip.subject diff --git a/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx b/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx index 5fb2275cb..168492abd 100644 --- a/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx +++ b/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx @@ -22,7 +22,7 @@ export default function ProductionBoardCard( ) { const { t } = useTranslation(); - let employee_body, employee_prep, employee_refinish; //employee_csr; + let employee_body, employee_prep, employee_refinish, employee_csr; if (card.employee_body) { employee_body = bodyshop.employees.find((e) => e.id === card.employee_body); } @@ -34,6 +34,9 @@ export default function ProductionBoardCard( (e) => e.id === card.employee_refinish ); } + if (card.employee_csr) { + employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr); + } // if (card.employee_csr) { // employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr); // } @@ -131,11 +134,11 @@ export default function ProductionBoardCard( )} ${employee_refinish.last_name.charAt(0)}` : "" } ${card.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`} - {/* {`C: ${ + {`C: ${ employee_csr ? `${employee_csr.first_name} ${employee_csr.last_name}` : "" - }`} */} + }`} )} diff --git a/client/src/components/production-board-kanban/production-board-kanban.utils.js b/client/src/components/production-board-kanban/production-board-kanban.utils.js index 4d2231621..4ed1a45d5 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.utils.js +++ b/client/src/components/production-board-kanban/production-board-kanban.utils.js @@ -66,6 +66,7 @@ export const createBoardData = (AllStatuses, Jobs, filter) => { include || j.employee_body === employeeId || j.employee_prep === employeeId || + j.employee_csr === employeeId || j.employee_refinish === employeeId; } @@ -76,9 +77,8 @@ export const createBoardData = (AllStatuses, Jobs, filter) => { Object.keys(DataGroupedByStatus).map((statusGroupKey) => { try { - boardLanes.columns.find( - (l) => l.id === statusGroupKey - ).cards = sortByParentId(DataGroupedByStatus[statusGroupKey]); + boardLanes.columns.find((l) => l.id === statusGroupKey).cards = + sortByParentId(DataGroupedByStatus[statusGroupKey]); } catch (error) { console.log("Error while creating board card", error); } diff --git a/client/src/components/production-list-columns/production-list-columns.empassignment.component.jsx b/client/src/components/production-list-columns/production-list-columns.empassignment.component.jsx index 606867975..e2ed28896 100644 --- a/client/src/components/production-list-columns/production-list-columns.empassignment.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.empassignment.component.jsx @@ -116,15 +116,17 @@ export function ProductionListEmpAssignment({ 0 } > - {bodyshop.employees.map((emp) => ( - - {`${emp.first_name} ${emp.last_name}`} - - ))} + {bodyshop.employees + .filter((emp) => emp.active) + .map((emp) => ( + + {`${emp.first_name} ${emp.last_name}`} + + ))} diff --git a/client/src/components/vendor-search-select/vendor-search-select.component.jsx b/client/src/components/vendor-search-select/vendor-search-select.component.jsx index 465007841..6537d0ded 100644 --- a/client/src/components/vendor-search-select/vendor-search-select.component.jsx +++ b/client/src/components/vendor-search-select/vendor-search-select.component.jsx @@ -1,12 +1,13 @@ import { HeartOutlined } from "@ant-design/icons"; -import { Select, Tag } from "antd"; +import { Select, Space, Tag } from "antd"; import React, { forwardRef, useEffect, useState } from "react"; +import PhoneNumberFormatter from "../../utils/PhoneFormatter"; const { Option } = Select; //To be used as a form element only. const VendorSearchSelect = ( - { value, onChange, options, onSelect, disabled, preferredMake }, + { value, onChange, options, onSelect, disabled, preferredMake, showPhone }, ref ) => { const [option, setOption] = useState(value); @@ -35,6 +36,7 @@ const VendorSearchSelect = ( style={{ width: "100%", }} + dropdownMatchSelectWidth={false} onChange={setOption} optionFilterProp="name" onSelect={onSelect} @@ -50,10 +52,15 @@ const VendorSearchSelect = ( >
{o.name}
- - {o.discount && o.discount !== 0 ? ( - {`${o.discount * 100}%`} - ) : null} + + + {o.phone && showPhone && ( + {o.phone} + )} + {o.discount && o.discount !== 0 ? ( + {`${o.discount * 100}%`} + ) : null} +
)) @@ -64,9 +71,14 @@ const VendorSearchSelect = (
{o.name}
- {o.discount && o.discount !== 0 ? ( - {`${o.discount * 100}%`} - ) : null} + + {o.phone && showPhone && ( + {o.phone} + )} + {o.discount && o.discount !== 0 ? ( + {`${o.discount * 100}%`} + ) : null} +
)) diff --git a/client/src/components/vendors-form/vendors-form.component.jsx b/client/src/components/vendors-form/vendors-form.component.jsx index b52739788..3fe8374e6 100644 --- a/client/src/components/vendors-form/vendors-form.component.jsx +++ b/client/src/components/vendors-form/vendors-form.component.jsx @@ -109,12 +109,14 @@ export default function VendorsFormComponent({ diff --git a/client/src/graphql/jobs-lines.queries.js b/client/src/graphql/jobs-lines.queries.js index cc4b59c04..3617df239 100644 --- a/client/src/graphql/jobs-lines.queries.js +++ b/client/src/graphql/jobs-lines.queries.js @@ -59,6 +59,8 @@ export const GET_LINE_TICKET_BY_PK = gql` employeeid memo flat_rate + clockon + clockoff employee { id first_name diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 9d0641d8a..c2b60a88c 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -696,6 +696,7 @@ export const GET_JOB_BY_PK = gql` joblineid bill { id + invoice_number vendor { id name diff --git a/client/src/graphql/vendors.queries.js b/client/src/graphql/vendors.queries.js index d4a3986cf..f18b124ff 100644 --- a/client/src/graphql/vendors.queries.js +++ b/client/src/graphql/vendors.queries.js @@ -87,6 +87,7 @@ export const QUERY_ALL_VENDORS_FOR_ORDER = gql` discount email active + phone } jobs(where: { id: { _eq: $jobId } }) { v_make_desc diff --git a/client/src/pages/courtesy-car-create/courtesy-car-create.page.container.jsx b/client/src/pages/courtesy-car-create/courtesy-car-create.page.container.jsx index 332b8127d..f373e4066 100644 --- a/client/src/pages/courtesy-car-create/courtesy-car-create.page.container.jsx +++ b/client/src/pages/courtesy-car-create/courtesy-car-create.page.container.jsx @@ -70,7 +70,12 @@ export function CourtesyCarCreateContainer({ return ( -
+
diff --git a/server.js b/server.js index 55b06de89..4b9d076f0 100644 --- a/server.js +++ b/server.js @@ -149,6 +149,7 @@ app.post( fb.unsubscribe ); app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser); +app.post("/adm/createuser", fb.validateFirebaseIdToken, fb.createUser); //Stripe Processing var stripe = require("./server/stripe/payment"); @@ -180,7 +181,6 @@ app.post("/data/arms", data.arms); var taskHandler = require("./server/tasks/tasks"); app.post("/taskHandler", taskHandler.taskHandler); - var ioevent = require("./server/ioevent/ioevent"); app.post("/ioevent", ioevent.default); app.post("/newlog", (req, res) => { @@ -188,7 +188,6 @@ app.post("/newlog", (req, res) => { logger.log(message, type, user, record, object); }); - var cdkGetMake = require("./server/cdk/cdk-get-makes"); app.post("/cdk/getvehicles", fb.validateFirebaseIdToken, cdkGetMake.default); diff --git a/server/firebase/firebase-handler.js b/server/firebase/firebase-handler.js index 42e6f19a6..05a5e38aa 100644 --- a/server/firebase/firebase-handler.js +++ b/server/firebase/firebase-handler.js @@ -25,6 +25,43 @@ const adminEmail = [ "patrick@thinkimex.com", ]; +exports.createUser = (req, res) => { + logger.log("admin-create-user", "WARN", req.user.email, null, { + request: req.body, + }); + if (!adminEmail.includes(req.user.email)) { + logger.log( + "admin-create-user-unauthorized", + "ERROR", + req.user.email, + null, + { + request: req.body, + user: req.user, + } + ); + res.sendStatus(404); + } + const { email, displayName, password } = req.body; + admin + .auth() + .createUser({ email, displayName, password }) + .then((userRecord) => { + // See the UserRecord reference doc for the contents of userRecord. + + logger.log("admin-update-user-success", "DEBUG", req.user.email, null, { + userRecord, + }); + res.json(userRecord); + }) + .catch((error) => { + logger.log("admin-update-user-error", "ERROR", req.user.email, null, { + error, + }); + res.status(500).json(error); + }); +}; + exports.updateUser = (req, res) => { logger.log("admin-update-user", "WARN", req.user.email, null, { request: req.body, diff --git a/server/scheduling/scheduling-job.js b/server/scheduling/scheduling-job.js index 24b5577b0..a314022c9 100644 --- a/server/scheduling/scheduling-job.js +++ b/server/scheduling/scheduling-job.js @@ -111,10 +111,9 @@ exports.job = async (req, res) => { } if ( - moment(item.actual_completion || item.scheduled_completion).tz(timezone).isBefore( - moment().tz(timezone), - "day" - ) + moment(item.actual_completion || item.scheduled_completion) + .tz(timezone) + .isBefore(moment().tz(timezone), "day") ) { console.log("Job should have already gone. Ignoring it.", item); return; @@ -128,7 +127,9 @@ exports.job = async (req, res) => { } else { const itemDate = moment( item.actual_completion || item.scheduled_completion - ).tz(timezone).format("yyyy-MM-DD"); + ) + .tz(timezone) + .format("yyyy-MM-DD"); if (!!load[itemDate]) { load[itemDate].hoursOut = (load[itemDate].hoursOut || 0) + @@ -153,14 +154,20 @@ exports.job = async (req, res) => { const end = moment.max([ ...filteredArrJobs.map((a) => moment(a.scheduled_in).tz(timezone)), ...filteredCompJobs - .map((p) => moment(p.actual_completion || p.scheduled_completion).tz(timezone)) + .map((p) => + moment(p.actual_completion || p.scheduled_completion).tz(timezone) + ) .filter((p) => p.isValid() && p.isAfter(yesterday)), moment().tz(timezone).add(15, "days"), ]); const range = Math.round(moment.duration(end.diff(today)).asDays()); for (var day = 0; day < range; day++) { - const current = moment(today).tz(timezone).add(day, "days").format("yyyy-MM-DD"); - const prev = moment(today).tz(timezone) + const current = moment(today) + .tz(timezone) + .add(day, "days") + .format("yyyy-MM-DD"); + const prev = moment(today) + .tz(timezone) .add(day - 1, "days") .format("yyyy-MM-DD"); if (!!!load[current]) { @@ -194,7 +201,7 @@ exports.job = async (req, res) => { load[startIsoFormat] = { blocked: true }; } }); - // //Propose the first 5 dates where we are below target. + // //Propose the first 10 dates where we are below target. const possibleDates = []; delete load.productionTotal; @@ -204,7 +211,7 @@ exports.job = async (req, res) => { loadKeys.forEach((loadKey) => { const isShopOpen = - (workingdays[dayOfWeekMapper(moment(loadKey).tz(timezone).day())] || false) && + (workingdays[dayOfWeekMapper(moment(loadKey).day())] || false) && !load[loadKey].blocked; if ( @@ -216,10 +223,10 @@ exports.job = async (req, res) => { possibleDates.push(new Date(loadKey).toISOString().substr(0, 10)); }); - if (possibleDates.length < 6) { + if (possibleDates.length < 11) { res.json(possibleDates); } else { - res.json(possibleDates.slice(0, 5)); + res.json(possibleDates.slice(0, 10)); } } catch (error) { logger.log("smart-scheduling-error", "ERROR", req.user.email, jobId, {