From 9cc0d6175e59c497292ad8077f0a37619442097e Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Thu, 15 Feb 2024 21:01:43 -0500 Subject: [PATCH 01/20] - Progress commit Signed-off-by: Dave Richer --- ...center-modal-filters-sorters-component.jsx | 24 ++++++++++++++----- client/src/utils/graphQLmodifier.js | 17 +++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) 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 index 62efefac0..4e3d0aa5d 100644 --- 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 @@ -5,8 +5,20 @@ import {DeleteFilled} from "@ant-design/icons"; import {useTranslation} from "react-i18next"; import {getOperatorsByType} from "../../utils/graphQLmodifier"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; +import {setModalContext} from "../../redux/modals/modals.actions"; +import {connect} from "react-redux"; +import {createStructuredSelector} from "reselect"; +import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors"; -export default function ReportCenterModalFiltersSortersComponent({form}) { +const mapDispatchToProps = (dispatch) => ({}); +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, + currentUser: selectCurrentUser +}); + +export function ReportCenterModalFiltersSortersComponent({form, bodyshop, currentUser}) { + console.dir(bodyshop, {depth: null}) + console.dir(currentUser, {depth: null}) return ( {() => { @@ -17,6 +29,9 @@ export default function ReportCenterModalFiltersSortersComponent({form}) { ); } +export default connect(mapStateToProps, mapDispatchToProps)(ReportCenterModalFiltersSortersComponent); + + function RenderFilters({templateId, form}) { const [state, setState] = useState(null); const [visible, setVisible] = useState(false); @@ -210,7 +225,7 @@ function RenderFilters({templateId, form}) { state.sorters ? state.sorters.map((f) => ({ value: f.name, - label: t(f.translation), + label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label, })) : [] } @@ -230,10 +245,7 @@ function RenderFilters({templateId, form}) { ]} > + { - form.setFieldsValue({[field.name]: {value: value.toString()}}); - }} - /> - } + } } @@ -245,7 +220,7 @@ function RenderFilters({templateId, form}) { ]} > + } + } + + // Number Input + if (type === "number") { + return {form.setFieldsValue({[field.name]: {value: parseInt(value)}}); + }} + /> + } + + // Default to String Input + return { + form.setFieldsValue({[field.name]: {value: value.toString()}}); + }} + /> +} + +export default connect(mapStateToProps, mapDispatchToProps)(ReportCenterModalValueSelectorComponent); \ No newline at end of file diff --git a/client/src/utils/RenderTemplate.js b/client/src/utils/RenderTemplate.js index 33121f6b0..0a6b16a38 100644 --- a/client/src/utils/RenderTemplate.js +++ b/client/src/utils/RenderTemplate.js @@ -9,7 +9,7 @@ import {store} from "../redux/store"; import client from "../utils/GraphQLClient"; import cleanAxios from "./CleanAxios"; import {TemplateList} from "./TemplateConstants"; -import {applyFilters, applySorters, parseQuery, printQuery, wrapFiltersInAnd} from "./graphQLmodifier"; +import {generateTemplate} from "./graphQLmodifier"; const server = process.env.REACT_APP_REPORTS_SERVER_URL; @@ -278,7 +278,9 @@ export const GenerateDocument = async ( sendType, jobid ) => { + const bodyshop = store.getState().user.bodyshop; + if (sendType === "e") { store.dispatch( setEmailOptions({ @@ -404,7 +406,7 @@ const fetchContextData = async (templateObject, jsrAuth) => { // We have no template filters or sorters, so we can just execute the query and return the data - if ((!templateObject?.filters && !templateObject?.filters?.length && !templateObject?.sorters && !templateObject?.sorters?.length)) { + if (!(templateObject?.filters?.length || templateObject?.sorters?.length)) { let contextData = {}; if (templateQueryToExecute) { const {data} = await client.query({ @@ -417,36 +419,11 @@ const fetchContextData = async (templateObject, jsrAuth) => { return {contextData, useShopSpecificTemplate}; } - // Parse the query and apply the filters and sorters - const ast = parseQuery(templateQueryToExecute); - - let filterFields = []; - - if (templateObject?.filters && templateObject?.filters?.length) { - applyFilters(ast, templateObject.filters, filterFields); - wrapFiltersInAnd(ast, filterFields); - } - - if (templateObject?.sorters && templateObject?.sorters?.length) { - applySorters(ast, templateObject.sorters); - } - - const finalQuery = printQuery(ast); - - // commented out for future revision debugging - // console.log('Modified Query'); - // console.log(finalQuery); - - let contextData = {}; - if (templateQueryToExecute) { - const {data} = await client.query({ - query: gql(finalQuery), - variables: {...templateObject.variables}, - }); - contextData = data; - } - - return {contextData, useShopSpecificTemplate}; + return await generateTemplate( + templateQueryToExecute, + templateObject, + useShopSpecificTemplate + ); }; //export const displayTemplateInWindow = (html) => { diff --git a/client/src/utils/graphQLmodifier.js b/client/src/utils/graphQLmodifier.js index c9a93542e..a269edd31 100644 --- a/client/src/utils/graphQLmodifier.js +++ b/client/src/utils/graphQLmodifier.js @@ -1,4 +1,6 @@ import {Kind, parse, print, visit} from "graphql"; +import client from "./GraphQLClient"; +import {gql} from "@apollo/client"; const STRING_OPERATORS = [ {value: "_eq", label: "equals"}, @@ -25,16 +27,23 @@ const ORDER_BY_OPERATORS = [ * Get the available operators for filtering * @returns {[{label: string, value: string},{label: string, value: string}]} */ -export function getOrderByOperators() { +export function getOrderOperatorsByType() { return ORDER_BY_OPERATORS; } +export function generateReflections(reflector) { + // const type = reflector?.type; + // const name = reflector?.name; + + return []; +} + /** * Get the available operators for filtering * @param type * @returns {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null]} */ -export function getOperatorsByType(type = 'string') { +export function getWhereOperatorsByType(type = 'string') { const operators = { string: STRING_OPERATORS, number: NUMBER_OPERATORS @@ -61,6 +70,50 @@ export function parseQuery(query) { export function printQuery(query) { return print(query); } + +/** + * Generate a template based on the query and object + * @param templateQueryToExecute + * @param templateObject + * @param useShopSpecificTemplate + * @returns {Promise<{contextData: {}, useShopSpecificTemplate}>} + */ +export async function generateTemplate(templateQueryToExecute, templateObject, useShopSpecificTemplate) { + // Advanced Filtering and Sorting modifications start here + + // Parse the query and apply the filters and sorters + const ast = parseQuery(templateQueryToExecute); + + let filterFields = []; + + if (templateObject?.filters && templateObject?.filters?.length) { + applyFilters(ast, templateObject.filters, filterFields); + wrapFiltersInAnd(ast, filterFields); + } + + if (templateObject?.sorters && templateObject?.sorters?.length) { + applySorters(ast, templateObject.sorters); + } + + const finalQuery = printQuery(ast); + + // commented out for future revision debugging + // console.log('Modified Query'); + // console.log(finalQuery); + + let contextData = {}; + if (templateQueryToExecute) { + const {data} = await client.query({ + query: gql(finalQuery), + variables: {...templateObject.variables}, + }); + contextData = data; + } + + return {contextData, useShopSpecificTemplate}; +} + + /** * Apply sorters to the AST * @param ast @@ -278,8 +331,6 @@ export function applyFilters(ast, filters) { }); } - - /** * Get the GraphQL kind for a value * @param value From 3b8e83d88a88a73be11b1d54d470c39331de0bd0 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Fri, 16 Feb 2024 12:32:40 -0500 Subject: [PATCH 03/20] - clear stage Signed-off-by: Dave Richer --- ...-center-modal-value-selector.component.jsx | 22 ++++++++++++++++--- client/src/utils/graphQLmodifier.js | 6 ----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/client/src/components/report-center-modal/report-center-modal-value-selector.component.jsx b/client/src/components/report-center-modal/report-center-modal-value-selector.component.jsx index a8db289aa..7e94badcc 100644 --- a/client/src/components/report-center-modal/report-center-modal-value-selector.component.jsx +++ b/client/src/components/report-center-modal/report-center-modal-value-selector.component.jsx @@ -1,4 +1,3 @@ -import {generateReflections} from "../../utils/graphQLmodifier"; import {Input, InputNumber, Select} from "antd"; import React from "react"; import {createStructuredSelector} from "reselect"; @@ -11,7 +10,10 @@ const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser }); + export function ReportCenterModalValueSelectorComponent ({type, reflector, form, field, bodyshop, currentUser}) { + + // TODO: Remove - used for debugging / development console.log(`Entering ReportCenterModalValueSelectorComponent`); console.log('Type') console.log(type) @@ -22,8 +24,17 @@ export function ReportCenterModalValueSelectorComponent ({type, reflector, for console.log('CurrentUser') console.dir(currentUser, {depth: null}) + function generateReflections(reflector) { + // const type = reflector?.type; + // const name = reflector?.name; + + return []; + } + + // We have a reflector, so we can generate a list of options if (reflector) { const reflections = generateReflections(reflector); + // We have options to display, so return a pre-populated select box if (reflections.length > 0) { return { - form.setFieldsValue({[field.name]: {value: value.toString()}}); + form.setFieldsValue({ + [field.name]: {value: value.toString()} + }); }} /> } diff --git a/client/src/utils/graphQLmodifier.js b/client/src/utils/graphQLmodifier.js index a269edd31..3d7e976f0 100644 --- a/client/src/utils/graphQLmodifier.js +++ b/client/src/utils/graphQLmodifier.js @@ -31,12 +31,6 @@ export function getOrderOperatorsByType() { return ORDER_BY_OPERATORS; } -export function generateReflections(reflector) { - // const type = reflector?.type; - // const name = reflector?.name; - - return []; -} /** * Get the available operators for filtering From a7e199932c905d0d6593dbe6b37c082a9b98c67d Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Fri, 16 Feb 2024 17:14:11 -0800 Subject: [PATCH 04/20] IO-2578 Scoreboard Entries Modal Correct OK button, add sorting to table, adjust date to only be a date, remove closeable on modal Signed-off-by: Allan Carr --- .../scoreboard-entry-edit.component.jsx | 27 ++++++++---- .../scoreboard-jobs-list.component.jsx | 41 ++++++++++++++----- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/client/src/components/scoreboard-entry-edit/scoreboard-entry-edit.component.jsx b/client/src/components/scoreboard-entry-edit/scoreboard-entry-edit.component.jsx index a8488e9dd..31141b628 100644 --- a/client/src/components/scoreboard-entry-edit/scoreboard-entry-edit.component.jsx +++ b/client/src/components/scoreboard-entry-edit/scoreboard-entry-edit.component.jsx @@ -1,5 +1,14 @@ import { useMutation } from "@apollo/client"; -import { Button, Card, Dropdown, Form, InputNumber, notification } from "antd"; +import { + Button, + Card, + Dropdown, + Form, + InputNumber, + notification, + Space, +} from "antd"; +import moment from "moment"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { UPDATE_SCOREBOARD_ENTRY } from "../../graphql/scoreboard.queries"; @@ -13,6 +22,7 @@ export default function ScoreboardEntryEdit({ entry }) { const handleFinish = async (values) => { setLoading(true); + values.date = moment(values.date).format("YYYY-MM-DD"); const result = await updateScoreboardentry({ variables: { sbId: entry.id, sbInput: values }, }); @@ -77,13 +87,14 @@ export default function ScoreboardEntryEdit({ entry }) { > - - - + + + + ); diff --git a/client/src/components/scoreboard-jobs-list/scoreboard-jobs-list.component.jsx b/client/src/components/scoreboard-jobs-list/scoreboard-jobs-list.component.jsx index 6b1c13ca3..67267fb3f 100644 --- a/client/src/components/scoreboard-jobs-list/scoreboard-jobs-list.component.jsx +++ b/client/src/components/scoreboard-jobs-list/scoreboard-jobs-list.component.jsx @@ -1,3 +1,4 @@ +import { SyncOutlined } from "@ant-design/icons"; import { useQuery } from "@apollo/client"; import { Button, Card, Input, Modal, Space, Table, Typography } from "antd"; import React, { useState } from "react"; @@ -5,12 +6,14 @@ import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { QUERY_SCOREBOARD_PAGINATED } from "../../graphql/scoreboard.queries"; import { DateFormatter } from "../../utils/DateFormatter"; +import { pageLimit } from "../../utils/config"; +import { alphaSort, dateSort } from "../../utils/sorters"; import AlertComponent from "../alert/alert.component"; -import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; +import OwnerNameDisplay, { + OwnerNameDisplayFunction, +} from "../owner-name-display/owner-name-display.component"; import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component"; import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component"; -import { SyncOutlined } from "@ant-design/icons"; -import {pageLimit} from "../../utils/config"; export default function ScoreboardJobsList({ scoreBoardlist }) { const { t } = useTranslation(); const [state, setState] = useState({ @@ -44,6 +47,7 @@ export default function ScoreboardJobsList({ scoreBoardlist }) { title: t("jobs.fields.ro_number"), dataIndex: "ro_number", key: "ro_number", + sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number), render: (text, record) => ( {record.job.ro_number || t("general.labels.na")} @@ -55,7 +59,11 @@ export default function ScoreboardJobsList({ scoreBoardlist }) { dataIndex: "owner", key: "owner", ellipsis: true, - + sorter: (a, b) => + alphaSort( + OwnerNameDisplayFunction(a.job), + OwnerNameDisplayFunction(b.job) + ), render: (text, record) => , }, { @@ -63,6 +71,15 @@ export default function ScoreboardJobsList({ scoreBoardlist }) { dataIndex: "vehicle", key: "vehicle", ellipsis: true, + sorter: (a, b) => + alphaSort( + `${a.job.v_model_yr || ""} ${a.job.v_make_desc || ""} ${ + a.job.v_model_desc || "" + }`, + `${b.job.v_model_yr || ""} ${b.job.v_make_desc || ""} ${ + b.job.v_model_desc || "" + }` + ), render: (text, record) => ( {`${record.job.v_model_yr || ""} ${ record.job.v_make_desc || "" @@ -73,17 +90,20 @@ export default function ScoreboardJobsList({ scoreBoardlist }) { title: t("scoreboard.fields.date"), dataIndex: "date", key: "date", + sorter: (a, b) => dateSort(a.date, b.date), render: (text, record) => {record.date}, }, - { - title: t("scoreboard.fields.painthrs"), - dataIndex: "painthrs", - key: "painthrs", - }, { title: t("scoreboard.fields.bodyhrs"), dataIndex: "bodyhrs", key: "bodyhrs", + sorter: (a, b) => Number(a.bodyhrs) - Number(b.bodyhrs), + }, + { + title: t("scoreboard.fields.painthrs"), + dataIndex: "painthrs", + key: "painthrs", + sorter: (a, b) => Number(a.painthrs) - Number(b.painthrs), }, { title: t("general.labels.actions"), @@ -104,8 +124,9 @@ export default function ScoreboardJobsList({ scoreBoardlist }) { visible={state.visible} destroyOnClose width="80%" + closable={false} cancelButtonProps={{ style: { display: "none" } }} - onCancel={() => + onOk={() => setState((state) => ({ ...state, visible: false, From 83bd485597e00a9303d5ff3a39fe5955b5d64c36 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Tue, 20 Feb 2024 09:19:30 -0800 Subject: [PATCH 05/20] IO-2557 New CC Contract Warnings Signed-off-by: Allan Carr --- .../contract-form/contract-form.component.jsx | 40 +++++++++++++++---- .../courtesy-cars-list.component.jsx | 9 ++++- client/src/graphql/courtesy-car.queries.js | 1 + client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/client/src/components/contract-form/contract-form.component.jsx b/client/src/components/contract-form/contract-form.component.jsx index f41933625..6f0125803 100644 --- a/client/src/components/contract-form/contract-form.component.jsx +++ b/client/src/components/contract-form/contract-form.component.jsx @@ -68,6 +68,30 @@ export default function ContractFormComponent({ )} + {create && ( + p.scheduledreturn !== c.scheduledreturn} + > + {() => { + const insuranceOver = + selectedCar && + selectedCar.insuranceexpires && + moment(selectedCar.insuranceexpires) + .endOf("day") + .isBefore(moment(form.getFieldValue("scheduledreturn"))); + if (insuranceOver) + return ( + + + + {t("contracts.labels.insuranceexpired")} + + + ); + return <>; + }} + + )} {() => { const mileageOver = - selectedCar && - selectedCar.nextservicekm <= form.getFieldValue("kmstart"); - + selectedCar && selectedCar.nextservicekm + ? selectedCar.nextservicekm <= form.getFieldValue("kmstart") + : false; const dueForService = selectedCar && selectedCar.nextservicedate && - moment(selectedCar.nextservicedate).isBefore( - moment(form.getFieldValue("scheduledreturn")) - ); - + moment(selectedCar.nextservicedate) + .endOf("day") + .isSameOrBefore( + moment(form.getFieldValue("scheduledreturn")) + ); if (mileageOver || dueForService) return ( @@ -117,7 +142,6 @@ export default function ContractFormComponent({ ); - return <>; }} diff --git a/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx b/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx index 2c992990c..d2c4e262d 100644 --- a/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx +++ b/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx @@ -72,7 +72,8 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) { sortOrder: state.sortedInfo.columnKey === "status" && state.sortedInfo.order, render: (text, record) => { - const { nextservicedate, nextservicekm, mileage } = record; + const { nextservicedate, nextservicekm, mileage, insuranceexpires } = + record; const mileageOver = nextservicekm ? nextservicekm <= mileage : false; @@ -80,10 +81,14 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) { nextservicedate && moment(nextservicedate).endOf("day").isSameOrBefore(moment()); + const insuranceOver = + insuranceexpires && + moment(insuranceexpires).endOf("day").isBefore(moment()); + return ( {t(record.status)} - {(mileageOver || dueForService) && ( + {(mileageOver || dueForService || insuranceOver) && ( diff --git a/client/src/graphql/courtesy-car.queries.js b/client/src/graphql/courtesy-car.queries.js index 4f7bcd0ca..2012e952e 100644 --- a/client/src/graphql/courtesy-car.queries.js +++ b/client/src/graphql/courtesy-car.queries.js @@ -29,6 +29,7 @@ export const QUERY_AVAILABLE_CC = gql` fleetnumber fuel id + insuranceexpires make mileage model diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 77cdb8091..76737f090 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -747,6 +747,7 @@ "driverinformation": "Driver's Information", "findcontract": "Find Contract", "findermodal": "Contract Finder", + "insuranceexpired": "The courtesy car insurance expires before the car is expected to return.", "noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.", "populatefromjob": "Populate from Job", "rates": "Contract Rates", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 341f86fd7..7527ab6ce 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -747,6 +747,7 @@ "driverinformation": "", "findcontract": "", "findermodal": "", + "insuranceexpired": "", "noteconvertedfrom": "", "populatefromjob": "", "rates": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 7cdabf620..dbb74ea69 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -747,6 +747,7 @@ "driverinformation": "", "findcontract": "", "findermodal": "", + "insuranceexpired": "", "noteconvertedfrom": "", "populatefromjob": "", "rates": "", From 06ef2482bae4b8681ba4d261a1ee5fa70b6dcda0 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Tue, 20 Feb 2024 12:53:12 -0800 Subject: [PATCH 06/20] IO-2562 CC Info in Job Block UI Correction Signed-off-by: Allan Carr --- .../jobs-detail-header.component.jsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx index 40c7aa4db..70dbc033a 100644 --- a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx +++ b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx @@ -123,11 +123,16 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) { {job.cccontracts.length > 0 && ( - {job.cccontracts.map((c) => ( - {`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`} + {job.cccontracts.map((c, index) => ( + + + {`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`} + {index !== job.cccontracts.length - 1 ? "," : null} + + ))} )} From 6b7b34ae798aa98e3edd71ce75a6cfd1ef120294 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Tue, 20 Feb 2024 16:00:59 -0500 Subject: [PATCH 07/20] - Progress Commit, this fills agreed upon functionality Signed-off-by: Dave Richer --- _reference/reportFiltersAndSorters.md | 34 +- ...center-modal-filters-sorters-component.jsx | 521 +++++++++++------- .../report-center-modal-utils.js | 123 +++++ ...-center-modal-value-selector.component.jsx | 64 --- .../report-center-modal.component.jsx | 8 +- client/src/utils/RenderTemplate.js | 2 + 6 files changed, 469 insertions(+), 283 deletions(-) create mode 100644 client/src/components/report-center-modal/report-center-modal-utils.js delete mode 100644 client/src/components/report-center-modal/report-center-modal-value-selector.component.jsx diff --git a/_reference/reportFiltersAndSorters.md b/_reference/reportFiltersAndSorters.md index bcaa08ade..b83491dbe 100644 --- a/_reference/reportFiltersAndSorters.md +++ b/_reference/reportFiltersAndSorters.md @@ -3,6 +3,9 @@ This documentation details the schema required for `.filters` files on the report server. It is used to dynamically modify the graphQL query and provide the user more power over their reports. +# Special Notes +- When passing the data to the template server, the property filters and sorters is added to the data object and will reflect the filters and sorters the user has selected + ## High level Schema Overview ```javascript @@ -36,6 +39,35 @@ const schema = { Filters effect the where clause of the graphQL query. They are used to filter the data returned from the server. A note on special notation used in the `name` field. +## Reflection +Filters can make use of reflection to pre-fill select boxes, the following is an example of that in the filters file. + +``` + { + "name": "jobs.status", + "translation": "jobs.fields.status", + "label": "Status", + "type": "string", + "reflector": { + "type": "internal", + "name": "special.job_statuses" + } + }, +``` + +in this example, a reflector with the type 'internal' (all types at the moment require this, and it is used for future functionality), with a name of `special.job_statuses` + +The following cases are available + +- `special.job_statuses` - This will reflect the statuses of the jobs table `bodyshop.md_ro_statuses.statuses'` +- `special.cost_centers` - This will reflect the cost centers `bodyshop.md_responsibility_centers.costs` +- `special.categories` - This will reflect the categories `bodyshop.md_categories` +- `special.insurance_companies` - This will reflect the insurance companies `bodyshop.md_ins_cos`' +- `special.employee_teams` - This will reflect the employee teams `bodyshop.employee_teams` +- `special.employees` - This will reflect the employees `bodyshop.employees` +- `special.first_names` - This will reflect the first names `bodyshop.employees` +- `special.last_names` - This will reflect the last names `bodyshop.employees` +- ### Path without brackets, multi level `"name": "jobs.joblines.mod_lb_hrs",` @@ -71,7 +103,6 @@ query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz! } ``` - ### Path with brackets,top level `"name": "[jobs].joblines.mod_lb_hrs",` This will produce a where clause at the `jobs` level of the graphQL query. @@ -114,7 +145,6 @@ query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz! - Do not add the ability to filter things that are already filtered as part of the original query, this would be redundant and could cause issues. - Do not add the ability to filter on things like FK constraints, must like the above example. - ## Sorters - Sorters follow the same schema as filters, however, they do not do square bracket wrapping to indicate level hoisting, a filter added on `job.md_status` would be added at the top level, and a filter added on `jobs.joblines.mod_lb_hrs` would be added at the `joblines` level. - Most of the reports currently do sorting on a template level, this will need to change to actually see the results using the sorters. 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 index 8882e82af..6d09b2fab 100644 --- 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 @@ -1,52 +1,336 @@ -import {Button, Card, Checkbox, Col, Form, Row, Select} from "antd"; -import React, {useEffect, useState} from "react"; +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"; -export default function ReportCenterModalFiltersSortersComponent({form}) { + +export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) { return ( {() => { const key = form.getFieldValue("key"); - return ; + return ; }} ); } -function RenderFilters({templateId, form}) { +/** + * Filters Section + * @param filters + * @param form + * @param bodyshop + * @returns {JSX.Element} + * @constructor + */ +function FiltersSection({filters, form, bodyshop}) { + const {t} = useTranslation(); + + return ( + + + {(fields, {add, remove, move}) => { + return ( +
+ {fields.map((field, index) => ( + + + + + + + } + } + + + + + { + () => { + 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; + + 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 + }); + }; + + const reflections = reflector ? generateReflections(reflector) : []; + + const fieldPath = [[field.name, "value"]]; + + if (reflections.length > 0) { + return ( + form.setFieldsValue(fieldPath, e.target.value)}/> + ); + })() + } + + } + } + + + + + { + remove(field.name); + }} + /> + + + + ))} + + + +
+ ); + }} +
+
+ ); +} + +/** + * Sorters Section + * @param sorters + * @param form + * @returns {JSX.Element} + * @constructor + */ +function SortersSection({sorters, form}) { + const {t} = useTranslation(); + return ( + + + {(fields, {add, remove, move}) => { + return ( +
+ Sorters + {fields.map((field, index) => ( + + + + + 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(); - useEffect(() => { - const fetch = async () => { - setIsLoading(true); - const data = await fetchFilterData({name: templateId}); - if (data?.success) { - setState(data.data); - } else { - setState(null); - } - setIsLoading(false); - }; + const fetch = useCallback(async () => { + // Reset all the filters and Sorters. + form.resetFields(['filters']); + form.resetFields(['sorters']); + setIsLoading(true); + + const data = await fetchFilterData({name: templateId}); + + if (data?.success) { + setState(data.data); + } else { + setState(null); + } + setIsLoading(false); + }, [templateId, form]); + + useEffect(() => { if (templateId) { fetch(); + } - }, [templateId]); + }, [templateId, fetch]); + const filters = useMemo(() => state?.filters || [], [state]); + const sorters = useMemo(() => state?.sorters || [], [state]); - // Conditional display of filters and sorters if (!templateId) return null; if (isLoading) return ; if (!state) return null; - // Filters and Sorters data available return (
{visible && (
- {state.filters && state.filters.length > 0 && ( - - - {(fields, {add, remove, move}) => { - return ( -
- {fields.map((field, index) => ( - - - - - - - } - } - - - - - - { - () => { - const name = form.getFieldValue(['filters', field.name, "field"]); - const type = state.filters.find(f => f.name === name)?.type; - const reflector = state.filters.find(f => f.name === name)?.reflector; - - return - - - } - } - - - - - { - remove(field.name); - }} - /> - - - - ))} - - - -
- ); - }} -
- -
+ {filters.length > 0 && ( + )} - {state.sorters && state.sorters.length > 0 && ( - - - {(fields, {add, remove, move}) => { - return ( -
- Sorters - {fields.map((field, index) => ( - - - - - - - - - - { - remove(field.name); - }} - /> - - - - ))} - - - -
- ); - }} -
-
+ {sorters.length > 0 && ( + )}
)} 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..3d94d9e8e --- /dev/null +++ b/client/src/components/report-center-modal/report-center-modal-utils.js @@ -0,0 +1,123 @@ +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); + +/** + * 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 + * @returns {{label: *, value: *}[]|{label: *, value: *}[]|{label: string, value: *}[]|*[]} + */ +const generateSpecialReflections = (bodyshop, finalPath) => { + switch (finalPath) { + case 'cost_centers': + return generateOptionsFromObject(bodyshop, 'md_responsibility_centers.costs', 'name', 'name'); + // Special case because Categories is an Array, not an Object. + case 'categories': + const catOptions = getValueFromPath(bodyshop, 'md_categories'); + return uniqBy(catOptions.map((value) => ({ + label: value, + value: value, + })), 'value'); + 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 + * @returns {{label: *, value: *}[]|[]|{label: *, value: *}[]|{label: string, value: *}[]|{label: *, value: *}[]|*[]} + */ +const generateInternalReflections = ({bodyshop, upperPath, finalPath}) => { + switch (upperPath) { + case 'special': + return generateSpecialReflections(bodyshop, finalPath); + 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-value-selector.component.jsx b/client/src/components/report-center-modal/report-center-modal-value-selector.component.jsx deleted file mode 100644 index 7e94badcc..000000000 --- a/client/src/components/report-center-modal/report-center-modal-value-selector.component.jsx +++ /dev/null @@ -1,64 +0,0 @@ -import {Input, InputNumber, Select} from "antd"; -import React from "react"; -import {createStructuredSelector} from "reselect"; -import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors"; -import {connect} from "react-redux"; - -const mapDispatchToProps = (dispatch) => ({}); -const mapStateToProps = createStructuredSelector({ - bodyshop: selectBodyshop, - currentUser: selectCurrentUser -}); - - -export function ReportCenterModalValueSelectorComponent ({type, reflector, form, field, bodyshop, currentUser}) { - - // TODO: Remove - used for debugging / development - console.log(`Entering ReportCenterModalValueSelectorComponent`); - console.log('Type') - console.log(type) - console.log('Reflector') - console.dir(reflector, {depth: null}) - console.log('Bodyshop') - console.dir(bodyshop, {depth: null}) - console.log('CurrentUser') - console.dir(currentUser, {depth: null}) - - function generateReflections(reflector) { - // const type = reflector?.type; - // const name = reflector?.name; - - return []; - } - - // We have a reflector, so we can generate a list of options - if (reflector) { - const reflections = generateReflections(reflector); - // We have options to display, so return a pre-populated select box - if (reflections.length > 0) { - return { - form.setFieldsValue({ - [field.name]: {value: value.toString()} - }); - }} - /> -} - -export default connect(mapStateToProps, mapDispatchToProps)(ReportCenterModalValueSelectorComponent); \ 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 af4ce7ef4..7fad5ddcf 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 @@ -16,9 +16,11 @@ import EmployeeSearchSelect from "../employee-search-select/employee-search-sele 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)) @@ -28,7 +30,7 @@ export default connect( mapDispatchToProps )(ReportCenterModalComponent); -export function ReportCenterModalComponent({reportCenterModal}) { +export function ReportCenterModalComponent({reportCenterModal, bodyshop}) { const [form] = Form.useForm(); const [search, setSearch] = useState(""); @@ -181,7 +183,7 @@ export function ReportCenterModalComponent({reportCenterModal}) { ); }} - + {() => { const key = form.getFieldValue("key"); diff --git a/client/src/utils/RenderTemplate.js b/client/src/utils/RenderTemplate.js index 0a6b16a38..b13228024 100644 --- a/client/src/utils/RenderTemplate.js +++ b/client/src/utils/RenderTemplate.js @@ -75,6 +75,8 @@ export default async function RenderTemplate( headerpath: `/${bodyshop.imexshopid}/header.html`, footerpath: `/${bodyshop.imexshopid}/footer.html`, bodyshop: bodyshop, + filters: templateObject?.filters, + sorters: templateObject?.sorters, offset: bodyshop.timezone, //dayjs().utcOffset(), }, }; From 33ec18986db7f9cfbef0522c82cb473f6481b155 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Tue, 20 Feb 2024 13:10:57 -0800 Subject: [PATCH 08/20] IO-2556 CC Sort Order Signed-off-by: Allan Carr --- client/src/graphql/courtesy-car.queries.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/graphql/courtesy-car.queries.js b/client/src/graphql/courtesy-car.queries.js index 4f7bcd0ca..bf1693ae0 100644 --- a/client/src/graphql/courtesy-car.queries.js +++ b/client/src/graphql/courtesy-car.queries.js @@ -22,6 +22,7 @@ export const QUERY_AVAILABLE_CC = gql` ] status: { _eq: "courtesycars.status.in" } } + order_by: { fleetnumber: asc } ) { color dailycost @@ -57,7 +58,7 @@ export const CHECK_CC_FLEET_NUMBER = gql` `; export const QUERY_ALL_CC = gql` query QUERY_ALL_CC { - courtesycars { + courtesycars(order_by: { fleetnumber: asc }) { color created_at dailycost From 6cfcab81561302f5cd79499fe8da1e3a8e9bf5c5 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Tue, 20 Feb 2024 14:52:40 -0800 Subject: [PATCH 09/20] IO-2557 / IO-1019 Update tooltip Signed-off-by: Allan Carr --- .../courtesy-cars-list.component.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx b/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx index d2c4e262d..e81a71e3e 100644 --- a/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx +++ b/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx @@ -89,7 +89,17 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) { {t(record.status)} {(mileageOver || dueForService || insuranceOver) && ( - + )} From 37708a0b591f0e13e8e144f5eefe05cbab44b23c Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Tue, 20 Feb 2024 19:07:23 -0500 Subject: [PATCH 10/20] - Default sorts! Signed-off-by: Dave Richer --- _reference/reportFiltersAndSorters.md | 17 ++++++++++ ...center-modal-filters-sorters-component.jsx | 17 +++++++++- .../report-center-modal.component.jsx | 31 ++++++++++++------- client/src/utils/RenderTemplate.js | 5 ++- client/src/utils/graphQLmodifier.js | 2 ++ 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/_reference/reportFiltersAndSorters.md b/_reference/reportFiltersAndSorters.md index b83491dbe..1bd950794 100644 --- a/_reference/reportFiltersAndSorters.md +++ b/_reference/reportFiltersAndSorters.md @@ -148,3 +148,20 @@ query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz! ## Sorters - Sorters follow the same schema as filters, however, they do not do square bracket wrapping to indicate level hoisting, a filter added on `job.md_status` would be added at the top level, and a filter added on `jobs.joblines.mod_lb_hrs` would be added at the `joblines` level. - Most of the reports currently do sorting on a template level, this will need to change to actually see the results using the sorters. + +### Default Sorters +- A sorter can be given a default object containing a `order` and `direction` key value. This will be used to sort the report if the user does not select any of the sorters themselves. +- The `order` key is the order in which the sorters are applied, and the `direction` key is the direction of the sort, either `asc` or `desc`. + +```json +{ + "name": "jobs.joblines.mod_lb_hrs", + "translation": "jobs.joblines.mod_lb_hrs_1", + "label": "mod_lb_hrs_1", + "type": "number", + "default": { + "order": 1, + "direction": "asc" + } +} +``` \ No newline at end of file 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 index 6d09b2fab..76ce5bb76 100644 --- 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 @@ -304,14 +304,29 @@ function RenderFilters({templateId, form, bodyshop}) { // 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); - } else { + } + // Something went wrong fetching filter data + else { setState(null); } setIsLoading(false); 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 7fad5ddcf..1dcf81481 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 @@ -16,7 +16,7 @@ import EmployeeSearchSelect from "../employee-search-select/employee-search-sele 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"; +import {selectBodyshop} from "../../redux/user/user.selectors"; const mapStateToProps = createStructuredSelector({ reportCenterModal: selectReportCenter, @@ -66,22 +66,28 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) { const end = values.dates ? values.dates[1] : null; const { id } = values; - await GenerateDocument( - { + 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") } : {}), + ...(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, @@ -119,7 +125,8 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) { onChange={(e) => setSearch(e.target.value)} value={search} /> - + } {...cardProps} >
@@ -220,6 +459,10 @@ export const DashboardScheduledInTodayGql = ` alt_transport clm_no jobid: id + joblines(where: {removed: {_eq: false}}) { + mod_lb_hrs + mod_lbr_ty + } ins_co_nm iouparent ownerid diff --git a/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx b/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx index 0407e3aad..ab1ab086f 100644 --- a/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx +++ b/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx @@ -3,37 +3,271 @@ import { ExclamationCircleFilled, PauseCircleOutlined, } from "@ant-design/icons"; -import { Card, Space, Table, Tooltip } from "antd"; +import { Card, Space, Switch, Table, Tooltip, Typography } from "antd"; import moment from "moment"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; +import { TimeFormatter } from "../../../utils/DateFormatter"; +import { onlyUnique } from "../../../utils/arrayHelper"; +import { alphaSort, dateSort } from "../../../utils/sorters"; +import useLocalStorage from "../../../utils/useLocalStorage"; import ChatOpenButton from "../../chat-open-button/chat-open-button.component"; -import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component"; +import OwnerNameDisplay, { + OwnerNameDisplayFunction, +} from "../../owner-name-display/owner-name-display.component"; import DashboardRefreshRequired from "../refresh-required.component"; -import {pageLimit} from "../../../utils/config"; export default function DashboardScheduledOutToday({ data, ...cardProps }) { const { t } = useTranslation(); const [state, setState] = useState({ sortedInfo: {}, }); + const [isTVMode_scheduled_out, setIsTVMode_scheduled_out] = useLocalStorage( + "isTVMode_scheduled_out", + false + ); + if (!data) return null; if (!data.scheduled_out_today) return ; data.scheduled_out_today.forEach((item) => { - item.scheduled_completion= moment(item.scheduled_completion).format("hh:mm a") + item.joblines_body = item.joblines + ? item.joblines + .filter((l) => l.mod_lbr_ty !== "LAR") + .reduce((acc, val) => acc + val.mod_lb_hrs, 0) + : 0; + item.joblines_ref = item.joblines + ? item.joblines + .filter((l) => l.mod_lbr_ty === "LAR") + .reduce((acc, val) => acc + val.mod_lb_hrs, 0) + : 0; }); data.scheduled_out_today.sort(function (a, b) { return new Date(a.scheduled_completion) - new Date(b.scheduled_completion); }); - const columns = [ + const TV_fontSize = 18; + const TV_fontWeight = "bold"; + + const tv_columns = [ + { + title: t("jobs.fields.scheduled_completion"), + dataIndex: "scheduled_completion", + key: "scheduled_completion", + ellipsis: true, + sorter: (a, b) => + dateSort(a.scheduled_completion, b.scheduled_completion), + sortOrder: + state.sortedInfo.columnKey === "scheduled_completion" && + state.sortedInfo.order, + render: (text, record) => ( + + {record.scheduled_completion} + + ), + }, { title: t("jobs.fields.ro_number"), dataIndex: "ro_number", key: "ro_number", + sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), + sortOrder: + state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, + render: (text, record) => ( + e.stopPropagation()} + > + + + {record.ro_number || t("general.labels.na")} + {record.production_vars && record.production_vars.alert ? ( + + ) : null} + {record.suspended && ( + + )} + {record.iouparent && ( + + + + )} + + + + ), + }, + { + title: t("jobs.fields.owner"), + dataIndex: "owner", + key: "owner", + ellipsis: true, + sorter: (a, b) => + alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)), + sortOrder: + state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, + render: (text, record) => { + return record.ownerid ? ( + e.stopPropagation()} + > + + + + + ) : ( + + + + ); + }, + }, + { + title: t("jobs.fields.vehicle"), + dataIndex: "vehicle", + key: "vehicle", + ellipsis: true, + sorter: (a, b) => + alphaSort( + `${a.v_model_yr || ""} ${a.v_make_desc || ""} ${ + a.v_model_desc || "" + }`, + `${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}` + ), + sortOrder: + state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order, + render: (text, record) => { + return record.vehicleid ? ( + e.stopPropagation()} + > + + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + + + ) : ( + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + ); + }, + }, + { + title: t("appointments.fields.alt_transport"), + dataIndex: "alt_transport", + key: "alt_transport", + ellipsis: true, + sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport), + sortOrder: + state.sortedInfo.columnKey === "alt_transport" && + state.sortedInfo.order, + filters: + (data.scheduled_out_today && + data.scheduled_out_today + .map((j) => j.alt_transport) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Alt. Transport*", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], + render: (text, record) => ( + + {record.alt_transport} + + ), + }, + { + title: t("jobs.fields.status"), + dataIndex: "status", + key: "status", + ellipsis: true, + sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport), + sortOrder: + state.sortedInfo.columnKey === "status" && state.sortedInfo.order, + filters: + (data.scheduled_out_today && + data.scheduled_out_today + .map((j) => j.status) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Status*", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], + render: (text, record) => ( + + {record.status} + + ), + }, + { + title: t("jobs.fields.lab"), + dataIndex: "joblines_body", + key: "joblines_body", + sorter: (a, b) => a.joblines_body - b.joblines_body, + sortOrder: + state.sortedInfo.columnKey === "joblines_body" && + state.sortedInfo.order, + align: "right", + render: (text, record) => ( + + {record.joblines_body} + + ), + }, + { + title: t("jobs.fields.lar"), + dataIndex: "joblines_ref", + key: "joblines_ref", + sorter: (a, b) => a.joblines_ref - b.joblines_ref, + sortOrder: + state.sortedInfo.columnKey === "joblines_ref" && state.sortedInfo.order, + align: "right", + render: (text, record) => ( + + {record.joblines_ref} + + ), + }, + ]; + + const columns = [ + { + title: t("jobs.fields.scheduled_completion"), + dataIndex: "scheduled_completion", + key: "scheduled_completion", + ellipsis: true, + sorter: (a, b) => + dateSort(a.scheduled_completion, b.scheduled_completion), + sortOrder: + state.sortedInfo.columnKey === "scheduled_completion" && + state.sortedInfo.order, + render: (text, record) => ( + {record.scheduled_completion} + ), + }, + { + title: t("jobs.fields.ro_number"), + dataIndex: "ro_number", + key: "ro_number", + sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), + sortOrder: + state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, render: (text, record) => ( + alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)), + sortOrder: + state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, render: (text, record) => { return record.ownerid ? ( ( - - ), - }, - { - title: t("jobs.fields.ownr_ph2"), - dataIndex: "ownr_ph2", - key: "ownr_ph2", - ellipsis: true, - responsive: ["md"], - render: (text, record) => ( - + + + + ), }, { @@ -104,7 +334,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { ellipsis: true, responsive: ["md"], render: (text, record) => ( - + {record.ownr_ea} ), }, { @@ -112,6 +342,15 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { dataIndex: "vehicle", key: "vehicle", ellipsis: true, + sorter: (a, b) => + alphaSort( + `${a.v_model_yr || ""} ${a.v_make_desc || ""} ${ + a.v_model_desc || "" + }`, + `${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}` + ), + sortOrder: + state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order, render: (text, record) => { return record.vehicleid ? ( alphaSort(a.ins_co_nm, b.ins_co_nm), + sortOrder: + state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order, + filters: + (data.scheduled_out_today && + data.scheduled_out_today + .map((j) => j.ins_co_nm) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Ins. Co.*", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], + onFilter: (value, record) => value.includes(record.ins_co_nm), }, { title: t("appointments.fields.alt_transport"), dataIndex: "alt_transport", key: "alt_transport", ellipsis: true, - responsive: ["md"], + sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport), + sortOrder: + state.sortedInfo.columnKey === "alt_transport" && + state.sortedInfo.order, + filters: + (data.scheduled_out_today && + data.scheduled_out_today + .map((j) => j.alt_transport) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Alt. Transport*", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], }, ]; @@ -158,20 +423,30 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { return ( + {t("general.labels.tvmode")} + setIsTVMode_scheduled_out(!isTVMode_scheduled_out)} + defaultChecked={isTVMode_scheduled_out} + /> + + } {...cardProps} >
@@ -188,6 +463,10 @@ export const DashboardScheduledOutTodayGql = ` alt_transport clm_no jobid: id + joblines(where: {removed: {_eq: false}}) { + mod_lb_hrs + mod_lbr_ty + } ins_co_nm iouparent ownerid @@ -200,6 +479,7 @@ export const DashboardScheduledOutTodayGql = ` production_vars ro_number scheduled_completion + status suspended v_make_desc v_model_desc diff --git a/client/src/components/dashboard-grid/dashboard-grid.component.jsx b/client/src/components/dashboard-grid/dashboard-grid.component.jsx index d24dd5641..7012d943b 100644 --- a/client/src/components/dashboard-grid/dashboard-grid.component.jsx +++ b/client/src/components/dashboard-grid/dashboard-grid.component.jsx @@ -275,26 +275,22 @@ const componentList = { h: 2, }, ScheduleInToday: { - label: i18next.t("dashboard.titles.scheduledintoday", { - date: moment().startOf("day").format("MM/DD/YYYY"), - }), + label: i18next.t("dashboard.titles.scheduledintoday"), component: DashboardScheduledInToday, gqlFragment: DashboardScheduledInTodayGql, - minW: 10, + minW: 6, minH: 2, w: 10, - h: 2, + h: 3, }, ScheduleOutToday: { - label: i18next.t("dashboard.titles.scheduledouttoday", { - date: moment().startOf("day").format("MM/DD/YYYY"), - }), + label: i18next.t("dashboard.titles.scheduledouttoday"), component: DashboardScheduledOutToday, gqlFragment: DashboardScheduledOutTodayGql, - minW: 10, + minW: 6, minH: 2, w: 10, - h: 2, + h: 3, }, }; @@ -306,8 +302,7 @@ const createDashboardQuery = (state) => { .map((item, index) => componentList[item.i].gqlFragment || "") .join(""); return gql` - query QUERY_DASHBOARD_DETAILS { - ${componentBasedAdditions || ""} + query QUERY_DASHBOARD_DETAILS { ${componentBasedAdditions || ""} monthly_sales: jobs(where: {_and: [ { voided: {_eq: false}}, {date_invoiced: {_gte: "${moment() @@ -317,11 +312,11 @@ const createDashboardQuery = (state) => { .endOf("month") .endOf("day") .toISOString()}"}}]}) { - id - ro_number - date_invoiced - job_totals - rate_la1 + id + ro_number + date_invoiced + job_totals + rate_la1 rate_la2 rate_la3 rate_la4 @@ -344,43 +339,42 @@ const createDashboardQuery = (state) => { rate_mapa rate_mash rate_matd - joblines(where: { removed: { _eq: false } }) { + joblines(where: { removed: { _eq: false } }) { id mod_lbr_ty mod_lb_hrs act_price part_qty part_type + } } - } - production_jobs: jobs(where: { inproduction: { _eq: true } }) { + production_jobs: jobs(where: { inproduction: { _eq: true } }) { + id + ro_number + ins_co_nm + job_totals + joblines(where: { removed: { _eq: false } }) { id - ro_number - ins_co_nm - job_totals - joblines(where: { removed: { _eq: false } }) { - id - mod_lbr_ty - mod_lb_hrs - act_price - part_qty - part_type - } - labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) { - aggregate { - sum { - mod_lb_hrs - } + mod_lbr_ty + mod_lb_hrs + act_price + part_qty + part_type + } + labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) { + aggregate { + sum { + mod_lb_hrs } } - larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) { - aggregate { - sum { - mod_lb_hrs - } + } + larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) { + aggregate { + sum { + mod_lb_hrs } } } } - `; + }`; }; diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 77cdb8091..dcbed3a58 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -874,6 +874,7 @@ "labels": { "bodyhrs": "Body Hrs", "dollarsinproduction": "Dollars in Production", + "phone": "Phone", "prodhrs": "Production Hrs", "refhrs": "Refinish Hrs" }, @@ -889,8 +890,10 @@ "productiondollars": "Total Dollars in Production", "productionhours": "Total Hours in Production", "projectedmonthlysales": "Projected Monthly Sales", - "scheduledintoday": "Sheduled In Today: {{date}}", - "scheduledouttoday": "Sheduled Out Today: {{date}}" + "scheduledindate": "Sheduled In Today: {{date}}", + "scheduledintoday": "Sheduled In Today:", + "scheduledoutdate": "Sheduled Out Today: {{date}}", + "scheduledouttoday": "Sheduled Out Today:" } }, "dms": { diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 341f86fd7..da6dad9b2 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -874,6 +874,7 @@ "labels": { "bodyhrs": "", "dollarsinproduction": "", + "phone": "", "prodhrs": "", "refhrs": "" }, @@ -889,7 +890,9 @@ "productiondollars": "", "productionhours": "", "projectedmonthlysales": "", + "scheduledindate": "", "scheduledintoday": "", + "scheduledoutdate": "", "scheduledouttoday": "" } }, diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 7cdabf620..85c29f96d 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -874,6 +874,7 @@ "labels": { "bodyhrs": "", "dollarsinproduction": "", + "phone": "", "prodhrs": "", "refhrs": "" }, @@ -889,7 +890,9 @@ "productiondollars": "", "productionhours": "", "projectedmonthlysales": "", + "scheduledindate": "", "scheduledintoday": "", + "scheduledoutdate": "", "scheduledouttoday": "" } }, From 8a01cd9cb0316c7540b4e6cfbf26cbbc20854325 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Wed, 21 Feb 2024 16:57:56 -0500 Subject: [PATCH 18/20] - call changes Signed-off-by: Dave Richer --- .../report-center-modal-filters-sorters-component.jsx | 10 +++++----- .../report-center-modal/report-center-modal-utils.js | 4 +--- client/src/translations/en_us/common.json | 5 +++++ client/src/translations/es/common.json | 5 +++++ client/src/translations/fr/common.json | 5 +++++ 5 files changed, 21 insertions(+), 8 deletions(-) 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 index 7255d5c13..470578ba2 100644 --- 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 @@ -42,7 +42,7 @@ function FiltersSection({filters, form, bodyshop}) { { } }; -export { - generateInternalReflections, -} \ No newline at end of file +export {generateInternalReflections,} \ No newline at end of file diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 77cdb8091..e6352c193 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -2579,6 +2579,11 @@ "advanced_filters_hide": "Hide", "advanced_filters_filters": "Filters", "advanced_filters_sorters": "Sorters", + "advanced_filters_filter_field": "Field", + "advanced_filters_sorter_field": "Field", + "advanced_filters_sorter_direction": "Direction", + "advanced_filters_filter_operator": "Operator", + "advanced_filters_filter_value": "Value", "dates": "Dates", "employee": "Employee", "filterson": "Filters on {{object}}: {{field}}", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 341f86fd7..f335dc37c 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2579,6 +2579,11 @@ "advanced_filters_hide": "", "advanced_filters_filters": "", "advanced_filters_sorters": "", + "advanced_filters_filter_field": "", + "advanced_filters_sorter_field": "", + "advanced_filters_sorter_direction": "", + "advanced_filters_filter_operator": "", + "advanced_filters_filter_value": "", "dates": "", "employee": "", "filterson": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 7cdabf620..ac74a0fd4 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -2579,6 +2579,11 @@ "advanced_filters_hide": "", "advanced_filters_filters": "", "advanced_filters_sorters": "", + "advanced_filters_filter_field": "", + "advanced_filters_sorter_field": "", + "advanced_filters_sorter_direction": "", + "advanced_filters_filter_operator": "", + "advanced_filters_filter_value": "", "dates": "", "employee": "", "filterson": "", From 4d1f40537cc55e1a5612773d3e62b7d95b954c7a Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 22 Feb 2024 12:57:48 -0800 Subject: [PATCH 19/20] IO-2640 Change Variable Names and adjust CSS Signed-off-by: Allan Carr --- .../scheduled-in-today.component.jsx | 47 +++++++++---------- .../scheduled-out-today.component.jsx | 42 ++++++++--------- .../dashboard-grid/dashboard-grid.styles.scss | 2 +- client/src/translations/en_us/common.json | 4 +- 4 files changed, 46 insertions(+), 49 deletions(-) diff --git a/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx b/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx index 268cb7a1c..3a902950e 100644 --- a/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx +++ b/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx @@ -23,8 +23,8 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { const [state, setState] = useState({ sortedInfo: {}, }); - const [isTVMode_scheduled_in, setIsTVMode_scheduled_in] = useLocalStorage( - "isTVMode_scheduled_in", + const [isTvModeScheduledIn, setIsTvModeScheduledIn] = useLocalStorage( + "isTvModeScheduledIn", false ); @@ -75,10 +75,10 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { return new moment(a.start) - new moment(b.start); }); - const TV_fontSize = 18; - const TV_fontWeight = "bold"; + const tvFontSize = 16; + const tvFontWeight = "bold"; - const tv_columns = [ + const tvColumns = [ { title: t("appointments.fields.time"), dataIndex: "start", @@ -88,7 +88,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { sortOrder: state.sortedInfo.columnKey === "start" && state.sortedInfo.order, render: (text, record) => ( - + {record.start} ), @@ -106,7 +106,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { onClick={(e) => e.stopPropagation()} > - + {record.ro_number || t("general.labels.na")} {record.production_vars && record.production_vars.alert ? ( @@ -139,12 +139,12 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { to={"/manage/owners/" + record.ownerid} onClick={(e) => e.stopPropagation()} > - + ) : ( - + ); @@ -170,18 +170,16 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { to={"/manage/vehicles/" + record.vehicleid} onClick={(e) => e.stopPropagation()} > - + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ record.v_model_desc || "" }`} ) : ( - {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ - record.v_model_desc || "" - }`} + {`${ + record.v_model_yr || "" + } ${record.v_make_desc || ""} ${record.v_model_desc || ""}`} ); }, }, @@ -208,7 +206,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { .sort((a, b) => alphaSort(a.text, b.text))) || [], render: (text, record) => ( - + {record.alt_transport} ), @@ -223,8 +221,8 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { state.sortedInfo.order, align: "right", render: (text, record) => ( - - {record.joblines_body} + + {record.joblines_body.toFixed(1)} ), }, @@ -237,8 +235,8 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { state.sortedInfo.columnKey === "joblines_ref" && state.sortedInfo.order, align: "right", render: (text, record) => ( - - {record.joblines_ref} + + {record.joblines_ref.toFixed(1)} ), }, @@ -414,7 +412,6 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { const handleTableChange = (sorter) => { setState({ ...state, sortedInfo: sorter }); }; - return ( {t("general.labels.tvmode")} setIsTVMode_scheduled_in(!isTVMode_scheduled_in)} - defaultChecked={isTVMode_scheduled_in} + onClick={() => setIsTvModeScheduledIn(!isTvModeScheduledIn)} + defaultChecked={isTvModeScheduledIn} /> } @@ -435,12 +432,12 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
diff --git a/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx b/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx index ab1ab086f..2a702a616 100644 --- a/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx +++ b/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx @@ -23,8 +23,8 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { const [state, setState] = useState({ sortedInfo: {}, }); - const [isTVMode_scheduled_out, setIsTVMode_scheduled_out] = useLocalStorage( - "isTVMode_scheduled_out", + const [isTvModeScheduledOut, setIsTvModeScheduledOut] = useLocalStorage( + "isTvModeScheduledOut", false ); @@ -48,10 +48,10 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { return new Date(a.scheduled_completion) - new Date(b.scheduled_completion); }); - const TV_fontSize = 18; - const TV_fontWeight = "bold"; + const tvFontSize = 18; + const tvFontWeight = "bold"; - const tv_columns = [ + const tvColumns = [ { title: t("jobs.fields.scheduled_completion"), dataIndex: "scheduled_completion", @@ -63,7 +63,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { state.sortedInfo.columnKey === "scheduled_completion" && state.sortedInfo.order, render: (text, record) => ( - + {record.scheduled_completion} ), @@ -81,7 +81,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { onClick={(e) => e.stopPropagation()} > - + {record.ro_number || t("general.labels.na")} {record.production_vars && record.production_vars.alert ? ( @@ -114,12 +114,12 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { to={"/manage/owners/" + record.ownerid} onClick={(e) => e.stopPropagation()} > - + ) : ( - + ); @@ -145,7 +145,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { to={"/manage/vehicles/" + record.vehicleid} onClick={(e) => e.stopPropagation()} > - + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ record.v_model_desc || "" }`} @@ -153,7 +153,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { ) : ( {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ record.v_model_desc || "" }`} @@ -183,7 +183,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { .sort((a, b) => alphaSort(a.text, b.text))) || [], render: (text, record) => ( - + {record.alt_transport} ), @@ -210,7 +210,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { .sort((a, b) => alphaSort(a.text, b.text))) || [], render: (text, record) => ( - + {record.status} ), @@ -225,8 +225,8 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { state.sortedInfo.order, align: "right", render: (text, record) => ( - - {record.joblines_body} + + {record.joblines_body.toFixed(1)} ), }, @@ -239,8 +239,8 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { state.sortedInfo.columnKey === "joblines_ref" && state.sortedInfo.order, align: "right", render: (text, record) => ( - - {record.joblines_ref} + + {record.joblines_ref.toFixed(1)} ), }, @@ -430,8 +430,8 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { {t("general.labels.tvmode")} setIsTVMode_scheduled_out(!isTVMode_scheduled_out)} - defaultChecked={isTVMode_scheduled_out} + onClick={() => setIsTvModeScheduledOut(!isTvModeScheduledOut)} + defaultChecked={isTvModeScheduledOut} /> } @@ -441,12 +441,12 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
diff --git a/client/src/components/dashboard-grid/dashboard-grid.styles.scss b/client/src/components/dashboard-grid/dashboard-grid.styles.scss index 62a3ae72b..140a1e1f3 100644 --- a/client/src/components/dashboard-grid/dashboard-grid.styles.scss +++ b/client/src/components/dashboard-grid/dashboard-grid.styles.scss @@ -128,7 +128,7 @@ height: 100%; width: 100%; .ant-card-body { - height: 80%; + height: calc(100% - 2rem); width: 100%; // // background-color: red; // height: 90%; diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index dcbed3a58..bcba064a0 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -891,9 +891,9 @@ "productionhours": "Total Hours in Production", "projectedmonthlysales": "Projected Monthly Sales", "scheduledindate": "Sheduled In Today: {{date}}", - "scheduledintoday": "Sheduled In Today:", + "scheduledintoday": "Sheduled In Today", "scheduledoutdate": "Sheduled Out Today: {{date}}", - "scheduledouttoday": "Sheduled Out Today:" + "scheduledouttoday": "Sheduled Out Today" } }, "dms": { From 3846b7c5fc5617439e1fbac85e69c865d61a5a87 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Fri, 23 Feb 2024 13:01:46 -0800 Subject: [PATCH 20/20] IO-2640 Adjust Filters and Sorters for Table Signed-off-by: Allan Carr --- .../scheduled-in-today.component.jsx | 8 ++++++-- .../scheduled-out-today.component.jsx | 16 +++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx b/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx index 3a902950e..fdf083a40 100644 --- a/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx +++ b/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx @@ -22,6 +22,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { const { t } = useTranslation(); const [state, setState] = useState({ sortedInfo: {}, + filteredInfo: {}, }); const [isTvModeScheduledIn, setIsTvModeScheduledIn] = useLocalStorage( "isTvModeScheduledIn", @@ -205,6 +206,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { }) .sort((a, b) => alphaSort(a.text, b.text))) || [], + onFilter: (value, record) => value.includes(record.alt_transport), render: (text, record) => ( {record.alt_transport} @@ -406,12 +408,14 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) { }) .sort((a, b) => alphaSort(a.text, b.text))) || [], + onFilter: (value, record) => value.includes(record.alt_transport), }, ]; - const handleTableChange = (sorter) => { - setState({ ...state, sortedInfo: sorter }); + const handleTableChange = (pagination, filters, sorter) => { + setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); }; + return ( ) : ( - {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ - record.v_model_desc || "" - }`} + {`${ + record.v_model_yr || "" + } ${record.v_make_desc || ""} ${record.v_model_desc || ""}`} ); }, }, @@ -182,6 +181,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { }) .sort((a, b) => alphaSort(a.text, b.text))) || [], + onFilter: (value, record) => value.includes(record.alt_transport), render: (text, record) => ( {record.alt_transport} @@ -209,6 +209,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { }) .sort((a, b) => alphaSort(a.text, b.text))) || [], + onFilter: (value, record) => value.includes(record.status), render: (text, record) => ( {record.status} @@ -414,11 +415,12 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) { }) .sort((a, b) => alphaSort(a.text, b.text))) || [], + onFilter: (value, record) => value.includes(record.alt_transport), }, ]; - const handleTableChange = (sorter) => { - setState({ ...state, sortedInfo: sorter }); + const handleTableChange = (pagination, filters, sorter) => { + setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); }; return (