{record.alt_transport}
@@ -382,7 +411,11 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
title: i18n.t("production.labels.alert"),
dataIndex: "alert",
key: "alert",
-
+ sorter: (a, b) =>
+ Number(a.production_vars?.alert || false) -
+ Number(b.production_vars?.alert || false),
+ sortOrder:
+ state.sortedInfo.columnKey === "alert" && state.sortedInfo.order,
render: (text, record) =>
,
},
{
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 e2ed28896..f3603c666 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
@@ -3,12 +3,12 @@ import { useMutation } from "@apollo/client";
import {
Button,
Col,
- notification,
Popover,
Row,
Select,
Space,
Spin,
+ notification,
} from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -25,8 +25,8 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
- insertAuditTrail: ({ jobid, operation }) =>
- dispatch(insertAuditTrail({ jobid, operation })),
+ insertAuditTrail: ({ jobid, operation, type }) =>
+ dispatch(insertAuditTrail({ jobid, operation, type })),
});
export function ProductionListEmpAssignment({
@@ -55,6 +55,7 @@ export function ProductionListEmpAssignment({
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.jobassignmentchange(empAssignment, name),
+ type: "jobassignmentchange",
});
if (!!result.errors) {
@@ -80,6 +81,7 @@ export function ProductionListEmpAssignment({
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.jobassignmentremoved(empAssignment),
+ type: "jobassignmentremoved",
});
if (!!result.errors) {
diff --git a/client/src/components/production-list-columns/production-list-columns.status.category.jsx b/client/src/components/production-list-columns/production-list-columns.status.category.jsx
index 115b69a3f..d49f1f348 100644
--- a/client/src/components/production-list-columns/production-list-columns.status.category.jsx
+++ b/client/src/components/production-list-columns/production-list-columns.status.category.jsx
@@ -12,8 +12,8 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
- insertAuditTrail: ({ jobid, operation }) =>
- dispatch(insertAuditTrail({ jobid, operation })),
+ insertAuditTrail: ({ jobid, operation, type }) =>
+ dispatch(insertAuditTrail({ jobid, operation, type })),
});
export function ProductionListColumnCategory({ record, bodyshop }) {
const [updateJob] = useMutation(UPDATE_JOB);
diff --git a/client/src/components/production-list-columns/production-list-columns.status.component.jsx b/client/src/components/production-list-columns/production-list-columns.status.component.jsx
index e29ffdbeb..b122bd3ae 100644
--- a/client/src/components/production-list-columns/production-list-columns.status.component.jsx
+++ b/client/src/components/production-list-columns/production-list-columns.status.component.jsx
@@ -5,16 +5,16 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
-import { selectBodyshop } from "../../redux/user/user.selectors";
import { insertAuditTrail } from "../../redux/application/application.actions";
+import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
- insertAuditTrail: ({ jobid, operation }) =>
- dispatch(insertAuditTrail({ jobid, operation })),
+ insertAuditTrail: ({ jobid, operation, type }) =>
+ dispatch(insertAuditTrail({ jobid, operation, type })),
});
export function ProductionListColumnStatus({
record,
@@ -40,6 +40,7 @@ export function ProductionListColumnStatus({
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.jobstatuschange(key),
+ type: "jobstatuschange",
});
setLoading(false);
diff --git a/client/src/components/report-center-modal/report-center-modal-filters-sorters-component.jsx b/client/src/components/report-center-modal/report-center-modal-filters-sorters-component.jsx
new file mode 100644
index 000000000..9a2daff8a
--- /dev/null
+++ b/client/src/components/report-center-modal/report-center-modal-filters-sorters-component.jsx
@@ -0,0 +1,432 @@
+import {Button, Card, Checkbox, Col, Form, Input, InputNumber, Row, Select} from "antd";
+import React, {useCallback, useEffect, useMemo, useState} from "react";
+import {fetchFilterData} from "../../utils/RenderTemplate";
+import {DeleteFilled} from "@ant-design/icons";
+import {useTranslation} from "react-i18next";
+import {getOrderOperatorsByType, getWhereOperatorsByType} from "../../utils/graphQLmodifier";
+import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
+import {generateInternalReflections} from "./report-center-modal-utils";
+import {FormDatePicker} from "../form-date-picker/form-date-picker.component.jsx";
+
+export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) {
+ return (
+
+ {() => {
+ const key = form.getFieldValue("key");
+ return ;
+ }}
+
+ );
+}
+
+/**
+ * Filters Section
+ * @param filters
+ * @param form
+ * @param bodyshop
+ * @returns {JSX.Element}
+ * @constructor
+ */
+function FiltersSection({filters, form, bodyshop}) {
+ const {t} = useTranslation();
+
+ return (
+
+
+ {(fields, {add, remove}) => {
+ return (
+
+ {fields.map((field, index) => (
+
+
+
+
+
+
+
+
+ {
+ () => {
+ const name = form.getFieldValue(['filters', field.name, "field"]);
+ const type = filters.find(f => f.name === name)?.type;
+
+ return
+
+ }
+ }
+
+
+
+
+ {
+ () => {
+ // Because it looks cleaner than inlining.
+ const name = form.getFieldValue(['filters', field.name, "field"]);
+ const type = filters.find(f => f.name === name)?.type;
+ const reflector = filters.find(f => f.name === name)?.reflector;
+ const operator = form.getFieldValue(['filters', field.name, "operator"]);
+ const operatorType = operator ? getWhereOperatorsByType(type).find((o) => o.value === operator)?.type : null;
+
+ return
+ {
+ (() => {
+ const generateReflections = (reflector) => {
+ if (!reflector) return [];
+
+ const {name} = reflector;
+ const path = name?.split('.');
+ const upperPath = path?.[0];
+ const finalPath = path?.slice(1).join('.');
+
+ return generateInternalReflections({
+ bodyshop,
+ upperPath,
+ finalPath,
+ t
+ });
+ };
+
+ const reflections = reflector ? generateReflections(reflector) : [];
+ const fieldPath = [[field.name, "value"]];
+ // We have reflections so we will use a select box
+ if (reflections.length > 0) {
+ // We have reflections and the operator type is array, so we will use a select box with multiple options
+ if (operatorType === "array") {
+ return (
+
+ }
+ }
+
+
+
+
+ {
+ remove(field.name);
+ }}
+ />
+
+
+
+ ))}
+
+
+
+
+ );
+ }}
+
+
+ );
+}
+
+/**
+ * Sorters Section
+ * @param sorters
+ * @param form
+ * @returns {JSX.Element}
+ * @constructor
+ */
+function SortersSection({sorters}) {
+ const {t} = useTranslation();
+ return (
+
+
+ {(fields, {add, remove}) => {
+ return (
+
+ Sorters
+ {fields.map((field, index) => (
+
+
+
+
+ ({
+ value: f.name,
+ label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
+ }))
+ }
+ getPopupContainer={trigger => trigger.parentNode}
+ />
+
+
+
+
+ trigger.parentNode}
+ />
+
+
+
+
+ {
+ remove(field.name);
+ }}
+ />
+
+
+
+ ))}
+
+
+
+
+ );
+ }}
+
+
+ );
+}
+
+/**
+ * Render Filters
+ * @param templateId
+ * @param form
+ * @param bodyshop
+ * @returns {JSX.Element|null}
+ * @constructor
+ */
+function RenderFilters({templateId, form, bodyshop}) {
+ const [state, setState] = useState(null);
+ const [visible, setVisible] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+ const {t} = useTranslation();
+
+ const fetch = useCallback(async () => {
+ // Reset all the filters and Sorters.
+ form.resetFields(['filters']);
+ form.resetFields(['sorters']);
+ form.resetFields(['defaultSorters']);
+
+ setIsLoading(true);
+
+ const data = await fetchFilterData({name: templateId});
+
+ // We have Success
+ if (data?.success) {
+ if (data?.data?.sorters && data?.data?.sorters.length > 0) {
+ const defaultSorters = data?.data?.sorters.filter((sorter) => sorter.hasOwnProperty('default')).map((sorter) => {
+ return {
+ field: sorter.name,
+ direction: sorter.default.direction
+ };
+ }).sort((a, b) => a.default.order - b.default.order);
+
+ form.setFieldValue('defaultSorters', JSON.stringify(defaultSorters));
+ }
+ // Set the state
+ setState(data.data);
+ }
+ // Something went wrong fetching filter data
+ else {
+ setState(null);
+ }
+ setIsLoading(false);
+ }, [templateId, form]);
+
+ useEffect(() => {
+ if (templateId) {
+ fetch();
+
+ }
+ }, [templateId, fetch]);
+
+ const filters = useMemo(() => state?.filters || [], [state]);
+ const sorters = useMemo(() => state?.sorters || [], [state]);
+
+ if (!templateId) return null;
+ if (isLoading) return
;
+ if (!state) return null;
+
+ return (
+
+
setVisible(e.target.checked)}
+ children={t('reportcenter.labels.advanced_filters')}
+ />
+ {visible && (
+
+ {filters.length > 0 && (
+
+ )}
+ {sorters.length > 0 && (
+
+ )}
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/components/report-center-modal/report-center-modal-utils.js b/client/src/components/report-center-modal/report-center-modal-utils.js
new file mode 100644
index 000000000..d78405cfd
--- /dev/null
+++ b/client/src/components/report-center-modal/report-center-modal-utils.js
@@ -0,0 +1,161 @@
+import {uniqBy} from "lodash";
+
+/**
+ * Get value from path
+ * @param obj
+ * @param path
+ * @returns {*}
+ */
+const getValueFromPath = (obj, path) => path.split('.').reduce((prev, curr) => prev?.[curr], obj);
+
+/**
+ * Generate options from array
+ * @param bodyshop
+ * @param path
+ * @returns {unknown[]}
+ */
+const generateOptionsFromArray = (bodyshop, path) => {
+ const options = getValueFromPath(bodyshop, path);
+ return uniqBy(options.map((value) => ({
+ label: value,
+ value: value,
+ })), 'value');
+}
+
+/**
+ * Valid internal reflections
+ * Note: This is intended for future functionality
+ * @type {{special: string[], bodyshop: [{name: string, type: string}]}}
+ */
+const VALID_INTERNAL_REFLECTIONS = {
+ bodyshop: [
+ {
+ name: 'md_ro_statuses.statuses',
+ type: 'kv-to-v'
+ }
+ ],
+};
+
+/**
+ * Generate options
+ * @param bodyshop
+ * @param path
+ * @param labelPath
+ * @param valuePath
+ * @returns {{label: *, value: *}[]}
+ */
+const generateOptionsFromObject = (bodyshop, path, labelPath, valuePath) => {
+ const options = getValueFromPath(bodyshop, path);
+ return uniqBy(Object.values(options).map((value) => ({
+ label: value[labelPath],
+ value: value[valuePath],
+ })), 'value');
+}
+
+/**
+ * Generate special reflections
+ * @param bodyshop
+ * @param finalPath
+ * @param t - i18n
+ * @returns {{label: *, value: *}[]|{label: *, value: *}[]|{label: string, value: *}[]|*[]}
+ */
+const generateSpecialReflections = (bodyshop, finalPath, t) => {
+ switch (finalPath) {
+ case 'payment_payers':
+ return [
+ {
+ label: t("payments.labels.customer"),
+ value: t("payments.labels.customer"),
+ },
+ {
+ label: t("payments.labels.insurance"),
+ value: t("payments.labels.insurance"),
+ },
+ // This is a weird one supposedly only used by one shop and could potentially be
+ // placed behind a SplitSDK
+ {
+ label: t("payments.labels.external"),
+ value: t("payments.labels.external"),
+ }
+ ];
+ case 'payment_types':
+ return generateOptionsFromArray(bodyshop, 'md_payment_types');
+ case 'alt_transports':
+ return generateOptionsFromArray(bodyshop, 'appt_alt_transport');
+ case 'lost_sale_reasons':
+ return generateOptionsFromArray(bodyshop, 'md_lost_sale_reasons');
+ // Special case because Referral Sources is an Array, not an Object.
+ case 'referral_source':
+ return generateOptionsFromArray(bodyshop, 'md_referral_sources');
+ case 'class':
+ return generateOptionsFromArray(bodyshop, 'md_classes');
+ case 'cost_centers':
+ return generateOptionsFromObject(bodyshop, 'md_responsibility_centers.costs', 'name', 'name');
+ // Special case because Categories is an Array, not an Object.
+ case 'categories':
+ return generateOptionsFromArray(bodyshop, 'md_categories');
+ case 'insurance_companies':
+ return generateOptionsFromObject(bodyshop, 'md_ins_cos', 'name', 'name');
+ case 'employee_teams':
+ return generateOptionsFromObject(bodyshop, 'employee_teams', 'name', 'id');
+ // Special case because Employees uses a concatenation of first_name and last_name
+ case 'employees':
+ const employeesOptions = getValueFromPath(bodyshop, 'employees');
+ return uniqBy(Object.values(employeesOptions).map((value) => ({
+ label: `${value.first_name} ${value.last_name}`,
+ value: value.id,
+ })), 'value');
+ case 'last_names':
+ return generateOptionsFromObject(bodyshop, 'employees', 'last_name', 'last_name');
+ case 'first_names':
+ return generateOptionsFromObject(bodyshop, 'employees', 'first_name', 'first_name');
+ case 'job_statuses':
+ const statusOptions = getValueFromPath(bodyshop, 'md_ro_statuses.statuses');
+ return Object.values(statusOptions).map((value) => ({
+ label: value,
+ value
+ }));
+ default:
+ console.error('Invalid Special reflection provided by Report Filters');
+ return [];
+ }
+}
+
+/**
+ * Generate bodyshop reflections
+ * @param bodyshop
+ * @param finalPath
+ * @returns {{label: *, value: *}[]|*[]}
+ */
+const generateBodyshopReflections = (bodyshop, finalPath) => {
+ const options = getValueFromPath(bodyshop, finalPath);
+ const reflectionRenderer = VALID_INTERNAL_REFLECTIONS.bodyshop.find(reflection => reflection.name === finalPath);
+ if (reflectionRenderer?.type === 'kv-to-v') {
+ return Object.values(options).map((value) => ({
+ label: value,
+ value
+ }));
+ }
+ return [];
+}
+
+/**
+ * Generate internal reflections based on the path and bodyshop
+ * @param bodyshop
+ * @param upperPath
+ * @param finalPath
+ * @param t - i18n
+ * @returns {{label: *, value: *}[]|[]|{label: *, value: *}[]|{label: string, value: *}[]|{label: *, value: *}[]|*[]}
+ */
+const generateInternalReflections = ({bodyshop, upperPath, finalPath, t}) => {
+ switch (upperPath) {
+ case 'special':
+ return generateSpecialReflections(bodyshop, finalPath, t);
+ case 'bodyshop':
+ return generateBodyshopReflections(bodyshop, finalPath);
+ default:
+ return [];
+ }
+};
+
+export {generateInternalReflections}
\ No newline at end of file
diff --git a/client/src/components/report-center-modal/report-center-modal.component.jsx b/client/src/components/report-center-modal/report-center-modal.component.jsx
index c4bef11c3..1a451f134 100644
--- a/client/src/components/report-center-modal/report-center-modal.component.jsx
+++ b/client/src/components/report-center-modal/report-center-modal.component.jsx
@@ -1,70 +1,64 @@
-import { useLazyQuery } from "@apollo/client";
-import {
- Button,
- Card,
- Col,
- DatePicker,
- Form,
- Input,
- Radio,
- Row,
- Typography,
-} from "antd";
+import {useLazyQuery} from "@apollo/client";
+import {Button, Card, Col, DatePicker, Form, Input, Radio, Row, Typography,} from "antd";
import _ from "lodash";
import moment from "moment";
-import React, { useState } from "react";
-import { useTranslation } from "react-i18next";
-import { connect } from "react-redux";
-import { createStructuredSelector } from "reselect";
-import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries";
-import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
-import { selectReportCenter } from "../../redux/modals/modals.selectors";
-import DatePIckerRanges from "../../utils/DatePickerRanges";
-import { GenerateDocument } from "../../utils/RenderTemplate";
-import { TemplateList } from "../../utils/TemplateConstants";
+import React, {useState} from "react";
+import {useTranslation} from "react-i18next";
+import {connect} from "react-redux";
+import {createStructuredSelector} from "reselect";
+import {QUERY_ACTIVE_EMPLOYEES} from "../../graphql/employees.queries";
+import {QUERY_ALL_VENDORS} from "../../graphql/vendors.queries";
+import {selectReportCenter} from "../../redux/modals/modals.selectors";
+import DatePickerRanges from "../../utils/DatePickerRanges";
+import {GenerateDocument} from "../../utils/RenderTemplate";
+import {TemplateList} from "../../utils/TemplateConstants";
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import "./report-center-modal.styles.scss";
+import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
+import {selectBodyshop} from "../../redux/user/user.selectors";
+
const mapStateToProps = createStructuredSelector({
- reportCenterModal: selectReportCenter,
+ reportCenterModal: selectReportCenter,
+ bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
- mapStateToProps,
- mapDispatchToProps
+ mapStateToProps,
+ mapDispatchToProps
)(ReportCenterModalComponent);
-export function ReportCenterModalComponent({ reportCenterModal }) {
+export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
const [form] = Form.useForm();
const [search, setSearch] = useState("");
const [loading, setLoading] = useState(false);
- const { t } = useTranslation();
+ const {t} = useTranslation();
const Templates = TemplateList("report_center");
const ReportsList = Object.keys(Templates).map((key) => {
return Templates[key];
});
- const { visible } = reportCenterModal;
+ const {open} = reportCenterModal;
- const [callVendorQuery, { data: vendorData, called: vendorCalled }] =
- useLazyQuery(QUERY_ALL_VENDORS, {
- skip: !(
- visible &&
- Templates[form.getFieldValue("key")] &&
- Templates[form.getFieldValue("key")].idtype
- ),
- });
+ const [callVendorQuery, {data: vendorData, called: vendorCalled}] =
+ useLazyQuery(QUERY_ALL_VENDORS, {
+ skip: !(
+ open &&
+ Templates[form.getFieldValue("key")] &&
+ Templates[form.getFieldValue("key")].idtype
+ ),
+ });
- const [callEmployeeQuery, { data: employeeData, called: employeeCalled }] =
- useLazyQuery(QUERY_ACTIVE_EMPLOYEES, {
- skip: !(
- visible &&
- Templates[form.getFieldValue("key")] &&
- Templates[form.getFieldValue("key")].idtype
- ),
- });
+ const [callEmployeeQuery, {data: employeeData, called: employeeCalled}] =
+ useLazyQuery(QUERY_ACTIVE_EMPLOYEES, {
+ skip: !(
+ open &&
+ Templates[form.getFieldValue("key")] &&
+ Templates[form.getFieldValue("key")].idtype
+ ),
+ });
const handleFinish = async (values) => {
setLoading(true);
@@ -72,244 +66,256 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
const end = values.dates ? values.dates[1] : null;
const { id } = values;
- await GenerateDocument(
- {
- name: values.key,
- variables: {
- ...(start
- ? { start: moment(start).startOf("day").format("YYYY-MM-DD") }
- : {}),
- ...(end
- ? { end: moment(end).endOf("day").format("YYYY-MM-DD") }
- : {}),
- ...(start ? { starttz: moment(start).startOf("day") } : {}),
- ...(end ? { endtz: moment(end).endOf("day") } : {}),
+ const templateConfig = {
+ name: values.key,
+ variables: {
+ ...(start
+ ? {start: moment(start).startOf("day").format("YYYY-MM-DD")}
+ : {}),
+ ...(end ? {end: moment(end).endOf("day").format("YYYY-MM-DD")} : {}),
+ ...(start ? {starttz: moment(start).startOf("day")} : {}),
+ ...(end ? {endtz: moment(end).endOf("day")} : {}),
- ...(id ? { id: id } : {}),
+ ...(id ? {id: id} : {}),
+ },
+ filters: values.filters,
+ sorters: values.sorters,
+ };
+
+ if (_.isString(values.defaultSorters) && !_.isEmpty(values.defaultSorters)) {
+ templateConfig.defaultSorters = JSON.parse(values.defaultSorters);
+ }
+
+ await GenerateDocument(
+ templateConfig,
+ {
+ to: values.to,
+ subject: Templates[values.key]?.subject,
},
- },
- {
- to: values.to,
- subject: Templates[values.key]?.subject,
- },
- values.sendbyexcel === "excel"
- ? "x"
- : values.sendby === "email"
- ? "e"
- : "p",
- id
+ values.sendbyexcel === "excel"
+ ? "x"
+ : values.sendby === "email"
+ ? "e"
+ : "p",
+ id
);
setLoading(false);
};
const FilteredReportsList =
- search !== ""
- ? ReportsList.filter((r) =>
- r.title.toLowerCase().includes(search.toLowerCase())
- )
- : ReportsList;
+ search !== ""
+ ? ReportsList.filter((r) =>
+ r.title.toLowerCase().includes(search.toLowerCase())
+ )
+ : ReportsList;
//Group it, create cards, and then filter out.
const grouped = _.groupBy(FilteredReportsList, "group");
return (
-
+ if (datedisable !== true) {
+ return (
+
+
+
+ );
+ } else return null;
+ }}
+
+
+ {() => {
+ const key = form.getFieldValue("key");
+ //Kind of Id
+ const reporttype = Templates[key] && Templates[key].reporttype;
+
+ if (reporttype === "excel")
+ return (
+
+
+ {t("general.labels.excel")}
+
+
+ );
+ if (reporttype !== "excel")
+ return (
+
+
+ {t("general.labels.email")}
+ {t("general.labels.print")}
+
+
+ );
+ }}
+
+
+
+
+
+
+
);
}
+
diff --git a/client/src/components/schedule-job-modal/schedule-job-modal.container.jsx b/client/src/components/schedule-job-modal/schedule-job-modal.container.jsx
index 19d6d4a37..c5a781fc0 100644
--- a/client/src/components/schedule-job-modal/schedule-job-modal.container.jsx
+++ b/client/src/components/schedule-job-modal/schedule-job-modal.container.jsx
@@ -34,8 +34,8 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("schedule")),
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
- insertAuditTrail: ({ jobid, operation }) =>
- dispatch(insertAuditTrail({ jobid, operation })),
+ insertAuditTrail: ({ jobid, operation, type }) =>
+ dispatch(insertAuditTrail({ jobid, operation, type })),
});
export function ScheduleJobModalContainer({
@@ -146,6 +146,7 @@ export function ScheduleJobModalContainer({
operation: AuditTrailMapping.appointmentinsert(
DateTimeFormat(values.start)
),
+ type: "appointmentinsert",
});
}
diff --git a/client/src/components/scoreboard-day-stats/scoreboard-day-stats.component.jsx b/client/src/components/scoreboard-day-stats/scoreboard-day-stats.component.jsx
index cfd6d945d..ecaf189fb 100644
--- a/client/src/components/scoreboard-day-stats/scoreboard-day-stats.component.jsx
+++ b/client/src/components/scoreboard-day-stats/scoreboard-day-stats.component.jsx
@@ -25,6 +25,8 @@ export function ScoreboardDayStats({ bodyshop, date, entries }) {
return acc + value.bodyhrs;
}, 0);
+ const numJobs = entries.length;
+
return (