From 8ea7db64886bf7096d9564e71dc5af8cb7735405 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 19 Mar 2024 08:10:45 -0700 Subject: [PATCH 001/134] WIP RO Closer. --- bodyshop_translations.babel | 21 +++ .../job-close-ro-gaurd.labor.jsx | 57 +++++++++ .../job-close-ro-guard.bills.jsx | 38 ++++++ .../job-close-ro-guard.container.jsx | 51 ++++++++ .../job-close-ro-guard.profit.jsx | 80 ++++++++++++ .../job-close-ro-guard.totals.jsx | 26 ++++ .../job-close-ro-guard.tt-lifecycle.jsx | 25 ++++ .../job-costing-statistics.component.jsx | 121 +++++++++--------- .../pages/jobs-close/jobs-close.component.jsx | 3 +- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 12 files changed, 364 insertions(+), 61 deletions(-) create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-gaurd.labor.jsx create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-guard.bills.jsx create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-guard.container.jsx create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-guard.profit.jsx create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-guard.totals.jsx create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-guard.tt-lifecycle.jsx diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index bac95454e..2734144d1 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -33645,6 +33645,27 @@ + + profitbypassrequired + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + prt_dsmk_total false diff --git a/client/src/components/job-close-ro-guard/job-close-ro-gaurd.labor.jsx b/client/src/components/job-close-ro-guard/job-close-ro-gaurd.labor.jsx new file mode 100644 index 000000000..71954458a --- /dev/null +++ b/client/src/components/job-close-ro-guard/job-close-ro-gaurd.labor.jsx @@ -0,0 +1,57 @@ +import React from 'react'; + +import { useQuery } from '@apollo/client'; +import { useSplitTreatments } from '@splitsoftware/splitio-react'; +import { connect } from 'react-redux'; +import { createStructuredSelector } from 'reselect'; +import { GET_LINE_TICKET_BY_PK } from '../../graphql/jobs-lines.queries'; +import { selectJobReadOnly } from '../../redux/application/application.selectors'; +import { selectBodyshop } from '../../redux/user/user.selectors'; +import AlertComponent from '../alert/alert.component'; +import LaborAllocationsTableComponent from '../labor-allocations-table/labor-allocations-table.component'; +import PayrollLaborAllocationsTable from '../labor-allocations-table/labor-allocations-table.payroll.component'; +import LoadingSkeleton from '../loading-skeleton/loading-skeleton.component'; +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, + jobRO: selectJobReadOnly, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardLabor); + +export function JobCloseRoGuardLabor({ job, jobRO, bodyshop, form }) { + const { loading, error, data, refetch } = useQuery(GET_LINE_TICKET_BY_PK, { + variables: { id: job.id }, + fetchPolicy: 'network-only', + nextFetchPolicy: 'network-only', + }); + const { + treatments: { Enhanced_Payroll }, + } = useSplitTreatments({ + attributes: {}, + names: ['Enhanced_Payroll'], + splitKey: bodyshop.imexshopid, + }); + + if (loading) return ; + if (error) return ; + + return Enhanced_Payroll.treatment === 'on' ? ( + + ) : ( + + ); +} diff --git a/client/src/components/job-close-ro-guard/job-close-ro-guard.bills.jsx b/client/src/components/job-close-ro-guard/job-close-ro-guard.bills.jsx new file mode 100644 index 000000000..c8949d873 --- /dev/null +++ b/client/src/components/job-close-ro-guard/job-close-ro-guard.bills.jsx @@ -0,0 +1,38 @@ +import React from 'react'; + +import { useQuery } from '@apollo/client'; +import { connect } from 'react-redux'; +import { createStructuredSelector } from 'reselect'; +import { QUERY_BILLS_BY_JOBID } from '../../graphql/bills.queries'; +import { selectJobReadOnly } from '../../redux/application/application.selectors'; +import { selectBodyshop } from '../../redux/user/user.selectors'; +import AlertComponent from '../alert/alert.component'; +import JobBillsTotalComponent from '../job-bills-total/job-bills-total.component'; +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, + jobRO: selectJobReadOnly, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardBills); + +export function JobCloseRoGuardBills({ job, jobRO, bodyshop, form }) { + const { loading, error, data } = useQuery(QUERY_BILLS_BY_JOBID, { + variables: { jobid: job.id }, + fetchPolicy: 'network-only', + nextFetchPolicy: 'network-only', + }); + + if (error) return ; + + return ( + + ); +} diff --git a/client/src/components/job-close-ro-guard/job-close-ro-guard.container.jsx b/client/src/components/job-close-ro-guard/job-close-ro-guard.container.jsx new file mode 100644 index 000000000..329e6a51e --- /dev/null +++ b/client/src/components/job-close-ro-guard/job-close-ro-guard.container.jsx @@ -0,0 +1,51 @@ +import React from 'react'; + +import { Col, Row } from 'antd'; +import { connect } from 'react-redux'; +import { createStructuredSelector } from 'reselect'; +import { selectJobReadOnly } from '../../redux/application/application.selectors'; +import { selectBodyshop } from '../../redux/user/user.selectors'; +import JobCloseRoGuardBills from './job-close-ro-guard.bills'; +import JobCloseRoGuardTotals from './job-close-ro-guard.totals'; +import JobCloseRoGuardLabor from './job-close-ro-gaurd.labor'; +import JobCloseRoGuardTtLifecycle from './job-close-ro-guard.tt-lifecycle'; +import JobCloseRoGuardProfit from './job-close-ro-guard.profit'; +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, + jobRO: selectJobReadOnly, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardContainer); + +const colSpans = { + md: 24, + lg: 12, + xl: 8, +}; +export function JobCloseRoGuardContainer({ job, jobRO, bodyshop, form }) { + return ( + + + + + + + + + + + AR Balance + + + + Sublet Manager + PPD Check + + + + + ); +} diff --git a/client/src/components/job-close-ro-guard/job-close-ro-guard.profit.jsx b/client/src/components/job-close-ro-guard/job-close-ro-guard.profit.jsx new file mode 100644 index 000000000..52d78dd73 --- /dev/null +++ b/client/src/components/job-close-ro-guard/job-close-ro-guard.profit.jsx @@ -0,0 +1,80 @@ +import React, { useEffect, useState } from 'react'; + +import axios from 'axios'; +import { connect } from 'react-redux'; +import { createStructuredSelector } from 'reselect'; +import { selectJobReadOnly } from '../../redux/application/application.selectors'; +import { selectBodyshop } from '../../redux/user/user.selectors'; +import JobCostingStatistics from '../job-costing-statistics/job-costing-statistics.component'; +import LoadingSkeleton from '../loading-skeleton/loading-skeleton.component'; +import { Card, Form, Input, Space } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { LockOutlined } from '@ant-design/icons'; + +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, + jobRO: selectJobReadOnly, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardProfit); + +export function JobCloseRoGuardProfit({ job, jobRO, bodyshop, form }) { + const [costingData, setCostingData] = useState(null); + const [loading, setLoading] = useState(false); + const { t } = useTranslation(); + + useEffect(() => { + async function getData() { + try { + if (job.id) { + setLoading(true); + const { data } = await axios.post('/job/costing', { jobid: job.id }); + setCostingData(data); + } + } catch (error) {} + setLoading(false); + } + + getData(); + }, [job.id]); + + if (loading || !costingData) return ; + + const enforceProfitPassword = parseFloat(costingData?.summaryData.gppercent) < 90; + return ( + + + + {' '} + {enforceProfitPassword && ( + ({ + validator(_, value) { + if ( + parseFloat(costingData?.summaryData.gppercent) < + bodyshop.md_ro_guard.totalgppercent_minimum && + value !== bodyshop.md_ro_guard.profitbypasspassword + ) { + console.log('We shoudl enforce it.'); + return Promise.reject(t('jobs.labels.profitbypassrequired')); + } + return Promise.resolve(); + }, + }), + ]} + > + } type="password" placeholder="Password" /> + + )} + + ); +} diff --git a/client/src/components/job-close-ro-guard/job-close-ro-guard.totals.jsx b/client/src/components/job-close-ro-guard/job-close-ro-guard.totals.jsx new file mode 100644 index 000000000..e765c941c --- /dev/null +++ b/client/src/components/job-close-ro-guard/job-close-ro-guard.totals.jsx @@ -0,0 +1,26 @@ +import React from 'react'; + +import { connect } from 'react-redux'; +import { createStructuredSelector } from 'reselect'; +import { selectJobReadOnly } from '../../redux/application/application.selectors'; +import { selectBodyshop } from '../../redux/user/user.selectors'; +import JobTotalsTableTotals from '../job-totals-table/job-totals.table.totals.component'; +import { Card } from 'antd'; +import { t } from 'i18next'; +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, + jobRO: selectJobReadOnly, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRGuardTotals); + +export function JobCloseRGuardTotals({ job, jobRO, bodyshop, form }) { + return ( + + + + ); +} diff --git a/client/src/components/job-close-ro-guard/job-close-ro-guard.tt-lifecycle.jsx b/client/src/components/job-close-ro-guard/job-close-ro-guard.tt-lifecycle.jsx new file mode 100644 index 000000000..b653d5ba8 --- /dev/null +++ b/client/src/components/job-close-ro-guard/job-close-ro-guard.tt-lifecycle.jsx @@ -0,0 +1,25 @@ +import React from 'react'; + +import { connect } from 'react-redux'; +import { createStructuredSelector } from 'reselect'; +import { selectJobReadOnly } from '../../redux/application/application.selectors'; +import { selectBodyshop } from '../../redux/user/user.selectors'; +import JobLifecycleComponent from '../job-lifecycle/job-lifecycle.component'; +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, + jobRO: selectJobReadOnly, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardTTLifeCycle); + +export function JobCloseRoGuardTTLifeCycle({ job, jobRO, bodyshop, form }) { + return ( +
+ Add Touch Time + +
+ ); +} diff --git a/client/src/components/job-costing-statistics/job-costing-statistics.component.jsx b/client/src/components/job-costing-statistics/job-costing-statistics.component.jsx index fc06e40eb..9f15a4850 100644 --- a/client/src/components/job-costing-statistics/job-costing-statistics.component.jsx +++ b/client/src/components/job-costing-statistics/job-costing-statistics.component.jsx @@ -1,63 +1,64 @@ -import {Statistic} from "antd"; -import React from "react"; -import {useTranslation} from "react-i18next"; -import Dinero from "dinero.js"; +import { Space, Statistic } from 'antd'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import Dinero from 'dinero.js'; -export default function JobCostingStatistics({summaryData}) { - const {t} = useTranslation(); +export default function JobCostingStatistics({ summaryData, onlyGP }) { + const { t } = useTranslation(); - return ( -
-
- - - - - - - - - - - - -
-
- ); + const gpTotals = ( + <> + + + + ); + if (onlyGP) return gpTotals; + + return ( + + + + + + + + + + + + + ); } diff --git a/client/src/pages/jobs-close/jobs-close.component.jsx b/client/src/pages/jobs-close/jobs-close.component.jsx index 44d9b5f86..c6d53c805 100644 --- a/client/src/pages/jobs-close/jobs-close.component.jsx +++ b/client/src/pages/jobs-close/jobs-close.component.jsx @@ -41,6 +41,7 @@ import {insertAuditTrail} from "../../redux/application/application.actions"; import {selectJobReadOnly} from "../../redux/application/application.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; +import JobCloseRoGuardContainer from '../../components/job-close-ro-guard/job-close-ro-guard.container'; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -193,7 +194,7 @@ export function JobsCloseComponent({job, bodyshop, jobRO, insertAuditTrail}) { } /> - + {!job.actual_in && job.scheduled_in && ( diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 334fdbc42..14bfe5670 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1973,6 +1973,7 @@ }, "ppc": "This line contains a part price change.", "profileadjustments": "Profile Disc./Mkup", + "profitbypassrequired": "Profit margin requirements not met. Bypass password required.", "prt_dsmk_total": "Line Item Adjustment", "rates": "Rates", "rates_subtotal": "All Rates Subtotal", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 3bdda9711..da6703881 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1973,6 +1973,7 @@ }, "ppc": "", "profileadjustments": "", + "profitbypassrequired": "", "prt_dsmk_total": "", "rates": "Tarifas", "rates_subtotal": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 8f180d52a..ae10b4070 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1973,6 +1973,7 @@ }, "ppc": "", "profileadjustments": "", + "profitbypassrequired": "", "prt_dsmk_total": "", "rates": "Les taux", "rates_subtotal": "", From 38aef71269b0347072156a2650133fb53115770a Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Wed, 20 Mar 2024 15:11:08 -0400 Subject: [PATCH 002/134] Progress --- .../components/header/header.component.jsx | 17 ++- .../task-list/task-list.component.jsx | 133 ++++++++++++++++++ .../task-list/task-list.container.jsx | 44 ++++++ client/src/graphql/bodyshop.queries.js | 19 +++ client/src/graphql/tasks.queries.js | 39 +++++ .../pages/manage/manage.page.component.jsx | 38 +++-- .../src/pages/tasks/tasks.page.component.jsx | 11 ++ .../src/pages/tasks/tasks.page.container.jsx | 47 +++++++ 8 files changed, 333 insertions(+), 15 deletions(-) create mode 100644 client/src/components/task-list/task-list.component.jsx create mode 100644 client/src/components/task-list/task-list.container.jsx create mode 100644 client/src/graphql/tasks.queries.js create mode 100644 client/src/pages/tasks/tasks.page.component.jsx create mode 100644 client/src/pages/tasks/tasks.page.container.jsx diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx index 20dae0de5..0dfac2ac6 100644 --- a/client/src/components/header/header.component.jsx +++ b/client/src/components/header/header.component.jsx @@ -30,7 +30,13 @@ import { Layout, Menu, Switch, Tooltip } from 'antd'; import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { BsKanban } from 'react-icons/bs'; -import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar } from 'react-icons/fa'; +import { + FaCalendarAlt, + FaCarCrash, + FaCreditCard, + FaFileInvoiceDollar, + FaTasks +} from 'react-icons/fa'; import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from 'react-icons/gi'; import { IoBusinessOutline } from 'react-icons/io5'; import { RiSurveyLine } from 'react-icons/ri'; @@ -329,9 +335,6 @@ function Header({ icon: , label: {t('menus.header.newjob')}, }, - { - type: 'divider', - }, { key: 'alljobs', icon: , @@ -463,6 +466,12 @@ function Header({ }, ] : []), + { + key: 'tasks', + id: 'tasks', + icon: , + label: {t('menus.header.tasks')}, + }, { key: 'shopsubmenu', icon: , diff --git a/client/src/components/task-list/task-list.component.jsx b/client/src/components/task-list/task-list.component.jsx new file mode 100644 index 000000000..bb56539a9 --- /dev/null +++ b/client/src/components/task-list/task-list.component.jsx @@ -0,0 +1,133 @@ +import {SyncOutlined} from "@ant-design/icons"; +import {Button, Card, Input, Space, Table, Typography} from "antd"; +import queryString from "query-string"; +import React, {useState} from "react"; +import {useTranslation} from "react-i18next"; +import {useLocation, useNavigate} from "react-router-dom"; +import {pageLimit} from "../../utils/config"; + +export default function TaskListComponent({ + loading, + tasks, + total, + refetch, + }) { + const search = queryString.parse(useLocation().search); + const { + page, + // sortcolumn, sortorder + } = search; + const history = useNavigate(); + + const [state, setState] = useState({ + sortedInfo: {}, + filteredInfo: {text: ""}, + }); + + const {t} = useTranslation(); + + const columns = [ + { + title: t("tasks.fields.title"), + dataIndex: "title", + key: "title", + }, + { + title: t("tasks.fields.description"), + dataIndex: "description", + key: "description", + }, + { + title: t("tasks.fields.due_date"), + dataIndex: "due_date", + key: "due_date", + }, + { + title: t("tasks.fields.assigned_to"), + dataIndex: "assigned_to", + key: "assigned_to", + }, + { + title: t("tasks.fields.completed"), + dataIndex: "completed", + key: "completed", + }, + { + title: t("tasks.fields.completed_at"), + dataIndex: "completed_at", + key: "completed_at", + }, + { + title: t("tasks.fields.remind_at"), + dataIndex: "remind_at", + key: "remind_at", + }, + { + title: t("tasks.fields.priority"), + dataIndex: "priority", + key: "priority", + }, + ]; + + const handleTableChange = (pagination, filters, sorter) => { + setState({...state, filteredInfo: filters, sortedInfo: sorter}); + search.page = pagination.current; + search.sortcolumn = sorter.columnKey; + search.sortorder = sorter.order; + history({search: queryString.stringify(search)}); + }; + return ( + + {search.search && ( + <> + + {t("general.labels.searchresults", {search: search.search})} + + + + )} + + { + if (value?.length >= 3) { + search.search = value; + } else { + delete search.search; + } + history({search: queryString.stringify(search)}); + }} + enterButton + /> + + } + > + + + ); +} diff --git a/client/src/components/task-list/task-list.container.jsx b/client/src/components/task-list/task-list.container.jsx new file mode 100644 index 000000000..3d188694d --- /dev/null +++ b/client/src/components/task-list/task-list.container.jsx @@ -0,0 +1,44 @@ +import queryString from "query-string"; +import {useLocation} from "react-router-dom"; +import {useQuery} from "@apollo/client"; +import {QUERY_ALL_TASKS_PAGINATED} from "../../graphql/tasks.queries.js"; +import {pageLimit} from "../../utils/config.js"; +import AlertComponent from "../alert/alert.component.jsx"; +import React from "react"; +import TaskListComponent from "./task-list.component.jsx"; + +export default function TaskListContainer() { + const searchParams = queryString.parse(useLocation().search); + const {page, sortcolumn, sortorder, search} = searchParams; + const {loading, error, data, refetch} = useQuery( + QUERY_ALL_TASKS_PAGINATED, + { + fetchPolicy: "network-only", + nextFetchPolicy: "network-only", + variables: { + offset: page ? (page - 1) * pageLimit : 0, + limit: pageLimit, + order: [ + { + [sortcolumn || "created_at"]: sortorder + ? sortorder === "descend" + ? "desc" + : "asc" + : "desc", + }, + ], + }, + } + ); + + if (error) return ; + + return ( + + ); +} diff --git a/client/src/graphql/bodyshop.queries.js b/client/src/graphql/bodyshop.queries.js index a8efb8d0b..5de9d7521 100644 --- a/client/src/graphql/bodyshop.queries.js +++ b/client/src/graphql/bodyshop.queries.js @@ -160,6 +160,25 @@ export const QUERY_BODYSHOP = gql` external_id flat_rate } + tasks { + id + created_at + updated_at + title + description + deleted + deleted_at + due_date + created_by + assigned_to + completed + completed_at + remind_at + priority + jobid + joblineid + partsorderid + } } } `; diff --git a/client/src/graphql/tasks.queries.js b/client/src/graphql/tasks.queries.js new file mode 100644 index 000000000..5ddaef3c1 --- /dev/null +++ b/client/src/graphql/tasks.queries.js @@ -0,0 +1,39 @@ +import {gql} from "@apollo/client"; + +export const QUERY_ALL_TASKS_PAGINATED = gql` + query QUERY_ALL_TASKS_PAGINATED( + $offset: Int + $limit: Int + $order: [tasks_order_by!]! + ) { + tasks( + offset: $offset + limit: $limit + order_by: $order + ) { + id + created_at + updated_at + title + description + deleted + deleted_at + due_date + created_by + assigned_to + completed + completed_at + remind_at + priority + jobid + joblineid + partsorderid + billid + } + tasks_aggregate(args: {}) { + aggregate { + count(distinct: true) + } + } + } +`; diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx index 3dd482974..e29f462ff 100644 --- a/client/src/pages/manage/manage.page.component.jsx +++ b/client/src/pages/manage/manage.page.component.jsx @@ -1,10 +1,10 @@ -import { FloatButton, Layout, Spin } from "antd"; +import {FloatButton, Layout, Spin} from "antd"; // import preval from "preval.macro"; -import React, { lazy, Suspense, useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { connect } from "react-redux"; -import { Link, Route, Routes } from "react-router-dom"; -import { createStructuredSelector } from "reselect"; +import React, {lazy, Suspense, useEffect, useState} from "react"; +import {useTranslation} from "react-i18next"; +import {connect} from "react-redux"; +import {Link, Route, Routes} from "react-router-dom"; +import {createStructuredSelector} from "reselect"; import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component"; import ChatAffixContainer from "../../components/chat-affix/chat-affix.container"; import ConflictComponent from "../../components/conflict/conflict.component"; @@ -17,14 +17,18 @@ import TestComponent from "../../components/_test/test.page"; import HeaderContainer from "../../components/header/header.container"; import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; import PartnerPingComponent from "../../components/partner-ping/partner-ping.component"; -import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container"; +import PrintCenterModalContainer + from "../../components/print-center-modal/print-center-modal.container"; import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component"; -import { requestForToken } from "../../firebase/firebase.utils"; -import { selectBodyshop, selectInstanceConflict, } from "../../redux/user/user.selectors"; +import {requestForToken} from "../../firebase/firebase.utils"; +import {selectBodyshop, selectInstanceConflict,} from "../../redux/user/user.selectors"; import UpdateAlert from "../../components/update-alert/update-alert.component"; -import { setJoyRideFinished } from "../../redux/application/application.actions.js"; -import { selectEnableJoyRide, selectJoyRideSteps } from "../../redux/application/application.selectors.js"; +import {setJoyRideFinished} from "../../redux/application/application.actions.js"; +import { + selectEnableJoyRide, + selectJoyRideSteps +} from "../../redux/application/application.selectors.js"; import InstanceRenderManager from "../../utils/instanceRenderMgr.js"; import "./manage.page.styles.scss"; @@ -180,6 +184,10 @@ const TtApprovals = lazy(() => import("../tt-approvals/tt-approvals.page.container") ); +const TasksPage = lazy(() => + import("../tasks/tasks.page.container") +); + const {Content, Footer} = Layout; @@ -286,7 +294,15 @@ export function Manage({conflict, bodyshop,enableJoyRide,joyRideSteps,setJoyRide }> }/> + {/*Start Task Routes */} }> + + } + /> + {/*End Task Routes */} + }> diff --git a/client/src/pages/tasks/tasks.page.component.jsx b/client/src/pages/tasks/tasks.page.component.jsx new file mode 100644 index 000000000..d1b80c856 --- /dev/null +++ b/client/src/pages/tasks/tasks.page.component.jsx @@ -0,0 +1,11 @@ +import React from "react"; +import TaskListContainer from "../../components/task-list/task-list.container.jsx"; + +export default function TasksPageComponent({bodyshop, currentUser}) { + + return ( +
+ +
+ ); +} diff --git a/client/src/pages/tasks/tasks.page.container.jsx b/client/src/pages/tasks/tasks.page.container.jsx new file mode 100644 index 000000000..c4151f1c6 --- /dev/null +++ b/client/src/pages/tasks/tasks.page.container.jsx @@ -0,0 +1,47 @@ +import React, {useEffect} from "react"; +import {useTranslation} from "react-i18next"; +import TasksPageComponent from "./tasks.page.component"; + +import {connect} from "react-redux"; +import {createStructuredSelector} from "reselect"; +import {setBreadcrumbs, setSelectedHeader} from "../../redux/application/application.actions"; +import InstanceRenderManager from "../../utils/instanceRenderMgr.js"; +import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors.js"; + +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, + currentUser: selectCurrentUser, +}); + +const mapDispatchToProps = (dispatch) => ({ + setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), + setSelectedHeader: (key) => dispatch(setSelectedHeader(key)), +}); + +export function TasksPageContainer({bodyshop, currentUser, setBreadcrumbs, setSelectedHeader}) { + const {t} = useTranslation(); + useEffect(() => { + document.title = t("titles.tasks", { + app: InstanceRenderManager({ + imex: '$t(titles.imexonline)', + rome: '$t(titles.romeonline)', + promanager: '$t(titles.promanager)' + }) + }); + setSelectedHeader("tasks"); + setBreadcrumbs([ + { + link: "/manage/tasks", + label: t("titles.bc.tasks"), + },]); + }, [t, setBreadcrumbs, setSelectedHeader]); + + return ( + + ); +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(TasksPageContainer); From f31ae9ac6d8d2a86d51d97d816770e8c92a52a24 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Wed, 20 Mar 2024 18:18:24 -0400 Subject: [PATCH 003/134] - Progress Commit Signed-off-by: Dave Richer --- .../task-list/task-list.component.jsx | 198 +++++++++++----- .../task-list/task-list.container.jsx | 47 +++- client/src/graphql/tasks.queries.js | 96 +++++++- .../src/pages/tasks/tasks.page.component.jsx | 2 +- hasura/metadata/actions.yaml | 10 +- hasura/metadata/allow_list.yaml | 2 +- hasura/metadata/cron_triggers.yaml | 2 +- hasura/metadata/network.yaml | 2 +- hasura/metadata/query_collections.yaml | 2 +- hasura/metadata/remote_schemas.yaml | 2 +- hasura/metadata/tables.yaml | 223 ++++++++++++++++-- 11 files changed, 488 insertions(+), 98 deletions(-) diff --git a/client/src/components/task-list/task-list.component.jsx b/client/src/components/task-list/task-list.component.jsx index bb56539a9..4a9aea817 100644 --- a/client/src/components/task-list/task-list.component.jsx +++ b/client/src/components/task-list/task-list.component.jsx @@ -1,74 +1,136 @@ -import {SyncOutlined} from "@ant-design/icons"; -import {Button, Card, Input, Space, Table, Typography} from "antd"; +import {Button, Card, Space, Switch, Table} from "antd"; import queryString from "query-string"; -import React, {useState} from "react"; +import React, {useCallback, useState} from "react"; import {useTranslation} from "react-i18next"; import {useLocation, useNavigate} from "react-router-dom"; import {pageLimit} from "../../utils/config"; +import dayjs from "../../utils/day"; +import { + CheckCircleFilled, + CheckCircleOutlined, + DeleteFilled, + DeleteOutlined, ExclamationCircleFilled, + SyncOutlined +} from "@ant-design/icons"; + +const DueDateRecord = ({dueDate}) => { + if (dueDate) { + const dueDateFormatted = dayjs(dueDate).format("YYYY-MM-DD"); + const relativeDueDate = dayjs(dueDate).fromNow(); + const today = dayjs().format("YYYY-MM-DD"); + if (dueDateFormatted < today) { + return
{dueDateFormatted}
; + } else { + return
{dueDateFormatted}
; + } + } else { + return
N/A
; + } +} +const PriorityLabel = ({priority}) => { + switch(priority) { + case 1: + return
+ High +
; + case 2: + return
+ Medium +
; + case 3: + return
+ Low +
; + default: + return
+ None +
; + } +} export default function TaskListComponent({ loading, tasks, total, refetch, + toggleCompletedStatus, }) { + const {t} = useTranslation(); + const search = queryString.parse(useLocation().search); + + // Extract Query Params const { page, - // sortcolumn, sortorder + sortcolumn, + sortorder, + deleted, + completed } = search; + const history = useNavigate(); - const [state, setState] = useState({ - sortedInfo: {}, - filteredInfo: {text: ""}, - }); - - const {t} = useTranslation(); - const columns = [ { title: t("tasks.fields.title"), dataIndex: "title", key: "title", + sorter: true, + sortOrder: sortcolumn === "title" && sortorder, }, { title: t("tasks.fields.description"), dataIndex: "description", key: "description", + sorter: true, + sortOrder: sortcolumn === "description" && sortorder, }, { title: t("tasks.fields.due_date"), dataIndex: "due_date", key: "due_date", - }, - { - title: t("tasks.fields.assigned_to"), - dataIndex: "assigned_to", - key: "assigned_to", - }, - { - title: t("tasks.fields.completed"), - dataIndex: "completed", - key: "completed", - }, - { - title: t("tasks.fields.completed_at"), - dataIndex: "completed_at", - key: "completed_at", - }, - { - title: t("tasks.fields.remind_at"), - dataIndex: "remind_at", - key: "remind_at", + sorter: true, + sortOrder: sortcolumn === "due_date" && sortorder, + width: '5%', + render: (text, record) => , }, { title: t("tasks.fields.priority"), dataIndex: "priority", key: "priority", + sorter: true, + sortOrder: sortcolumn === "priority" && sortorder, + width: '5%', + render: (text, record) => + }, + { + title: t("tasks.fields.actions"), + key: "toggleCompleted", + width: '5%', + render: (text, record) => ( + + + + + ), }, ]; + const [state, setState] = useState({ + sortedInfo: {}, + filteredInfo: {text: ""}, + }); + + // Columns definition remains the same + const handleTableChange = (pagination, filters, sorter) => { setState({...state, filteredInfo: filters, sortedInfo: sorter}); search.page = pagination.current; @@ -76,43 +138,49 @@ export default function TaskListComponent({ search.sortorder = sorter.order; history({search: queryString.stringify(search)}); }; + + const handleSwitchChange = (param, value) => { + if (value) { + search[param] = "true"; + } else { + delete search[param]; + } + history({search: queryString.stringify(search)}); + }; + + /** + * Extra actions for the tasks + * @type {Function} + */ + const tasksExtra = useCallback(() => { + return ( + + } + unCheckedChildren={} + title={t('tasks.titles.completed')} + checked={completed === "true"} + onChange={(value) => handleSwitchChange('completed', value)} + /> + } + unCheckedChildren={} + title={t('tasks.titles.deleted')} + checked={deleted === "true"} + onChange={(value) => handleSwitchChange('deleted', value)} + /> + + + ); + }, [refetch, deleted, completed]); + return ( - {search.search && ( - <> - - {t("general.labels.searchresults", {search: search.search})} - - - - )} - - { - if (value?.length >= 3) { - search.search = value; - } else { - delete search.search; - } - history({search: queryString.stringify(search)}); - }} - enterButton - /> - - } + extra={tasksExtra()} >
{ + const completed_at = !currentStatus ? new Date().toISOString() : null; + await toggleTaskCompleted({ + variables: { + id: id, + completed: !currentStatus, + completed_at: completed_at + } + }); + refetch(); + }; + + const [toggleTaskDeleted] = useMutation(MUTATION_TOGGLE_TASK_DELETED); + + const toggleDeletedStatus = async (id, currentStatus) => { + const deleted_at = !currentStatus ? new Date().toISOString() : null; + await toggleTaskDeleted({ + variables: { + id: id, + deleted: !currentStatus, + deleted_at: deleted_at + } + }); + refetch(); + }; if (error) return ; @@ -39,6 +74,8 @@ export default function TaskListContainer() { tasks={data ? data.tasks : null} total={data ? data.tasks_aggregate.aggregate.count : 0} refetch={refetch} + toggleCompletedStatus={toggleCompletedStatus} + toggleDeletedStatus={toggleDeletedStatus} /> ); } diff --git a/client/src/graphql/tasks.queries.js b/client/src/graphql/tasks.queries.js index 5ddaef3c1..0a4375b1d 100644 --- a/client/src/graphql/tasks.queries.js +++ b/client/src/graphql/tasks.queries.js @@ -1,5 +1,9 @@ import {gql} from "@apollo/client"; +/** + * All tasks paginated query + * @type {DocumentNode} + */ export const QUERY_ALL_TASKS_PAGINATED = gql` query QUERY_ALL_TASKS_PAGINATED( $offset: Int @@ -30,10 +34,98 @@ export const QUERY_ALL_TASKS_PAGINATED = gql` partsorderid billid } - tasks_aggregate(args: {}) { + tasks_aggregate { aggregate { - count(distinct: true) + count } } } `; + +/** + * My tasks paginated query + * @type {DocumentNode} + */ +export const QUERY_MY_TASKS_PAGINATED = gql` + query QUERY_TASKS_PAGINATED( + $offset: Int + $limit: Int + $user: String! + $bodyshop: uuid! + $deleted: Boolean + $completed: Boolean + $order: [tasks_order_by!]! + ) { + tasks( + offset: $offset + limit: $limit + order_by: $order + where: { + assigned_to: {_eq: $user}, + bodyshopid: {_eq: $bodyshop}, + deleted: {_eq: $deleted}, + completed: {_eq: $completed} + } + ) { + id + created_at + updated_at + title + description + deleted + deleted_at + due_date + created_by + assigned_to + completed + completed_at + remind_at + priority + jobid + joblineid + partsorderid + billid + bodyshopid + } + tasks_aggregate( + where: { + assigned_to: {_eq: $user}, + bodyshopid: {_eq: $bodyshop}, + deleted: {_eq: $deleted}, + completed: {_eq: $completed} + } + ) { + aggregate { + count + } + } + } +`; + +/** + * Toggle task completed mutation + * @type {DocumentNode} + */ +export const MUTATION_TOGGLE_TASK_COMPLETED = gql` + mutation MUTATION_TOGGLE_TASK_COMPLETED($id: uuid!, $completed: Boolean!, $completed_at: timestamptz) { + update_tasks_by_pk(pk_columns: {id: $id}, _set: {completed: $completed, completed_at: $completed_at}) { + id + completed + completed_at + } + } +`; + +/** + * Toggle task deleted mutation + * @type {DocumentNode} + */ +export const MUTATION_TOGGLE_TASK_DELETED = gql` + mutation MUTATION_TOGGLE_TASK_DELETED($id: uuid!, $deleted: Boolean!, $deleted_at: timestamptz) { + update_tasks_by_pk(pk_columns: {id: $id}, _set: {deleted: $deleted, deleted_at: $deleted_at}) { + id + deleted + deleted_at + } + } +`; diff --git a/client/src/pages/tasks/tasks.page.component.jsx b/client/src/pages/tasks/tasks.page.component.jsx index d1b80c856..253bc9c4c 100644 --- a/client/src/pages/tasks/tasks.page.component.jsx +++ b/client/src/pages/tasks/tasks.page.component.jsx @@ -5,7 +5,7 @@ export default function TasksPageComponent({bodyshop, currentUser}) { return (
- +
); } diff --git a/hasura/metadata/actions.yaml b/hasura/metadata/actions.yaml index f5639566d..1edb4c2ff 100644 --- a/hasura/metadata/actions.yaml +++ b/hasura/metadata/actions.yaml @@ -1,6 +1,6 @@ -actions: [ ] +actions: [] custom_types: - enums: [ ] - input_objects: [ ] - objects: [ ] - scalars: [ ] + enums: [] + input_objects: [] + objects: [] + scalars: [] diff --git a/hasura/metadata/allow_list.yaml b/hasura/metadata/allow_list.yaml index 1e3ec7217..fe51488c7 100644 --- a/hasura/metadata/allow_list.yaml +++ b/hasura/metadata/allow_list.yaml @@ -1 +1 @@ -[ ] +[] diff --git a/hasura/metadata/cron_triggers.yaml b/hasura/metadata/cron_triggers.yaml index 1e3ec7217..fe51488c7 100644 --- a/hasura/metadata/cron_triggers.yaml +++ b/hasura/metadata/cron_triggers.yaml @@ -1 +1 @@ -[ ] +[] diff --git a/hasura/metadata/network.yaml b/hasura/metadata/network.yaml index ffcd4415b..0967ef424 100644 --- a/hasura/metadata/network.yaml +++ b/hasura/metadata/network.yaml @@ -1 +1 @@ -{ } +{} diff --git a/hasura/metadata/query_collections.yaml b/hasura/metadata/query_collections.yaml index 1e3ec7217..fe51488c7 100644 --- a/hasura/metadata/query_collections.yaml +++ b/hasura/metadata/query_collections.yaml @@ -1 +1 @@ -[ ] +[] diff --git a/hasura/metadata/remote_schemas.yaml b/hasura/metadata/remote_schemas.yaml index 1e3ec7217..fe51488c7 100644 --- a/hasura/metadata/remote_schemas.yaml +++ b/hasura/metadata/remote_schemas.yaml @@ -1 +1 @@ -[ ] +[] diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 9cc14dd54..38dff43fa 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -53,7 +53,7 @@ update_permissions: - role: user permission: - columns: [ ] + columns: [] filter: jobline: job: @@ -569,6 +569,13 @@ table: name: parts_orders schema: public + - name: tasks + using: + foreign_key_constraint_on: + column: billid + table: + name: tasks + schema: public insert_permissions: - role: user permission: @@ -698,7 +705,7 @@ value_from_env: EVENT_SECRET request_transform: method: POST - query_params: { } + query_params: {} template_engine: Kriti url: '{{$base_url}}/opensearch' version: 2 @@ -818,6 +825,13 @@ table: name: inventory schema: public + - name: ioevents + using: + foreign_key_constraint_on: + column: bodyshopid + table: + name: ioevents + schema: public - name: jobs using: foreign_key_constraint_on: @@ -846,6 +860,13 @@ table: name: phonebook schema: public + - name: tasks + using: + foreign_key_constraint_on: + column: bodyshopid + table: + name: tasks + schema: public - name: timetickets using: foreign_key_constraint_on: @@ -1653,7 +1674,7 @@ columns: - config - id - filter: { } + filter: {} limit: 1 - role: user permission: @@ -2491,7 +2512,7 @@ - effective_date - end_date - content - filter: { } + filter: {} - table: name: exportlog schema: public @@ -2675,6 +2696,13 @@ - table: name: ioevents schema: public + object_relationships: + - name: bodyshop + using: + foreign_key_constraint_on: bodyshopid + - name: user + using: + foreign_key_constraint_on: useremail - table: name: job_ar_schema schema: public @@ -2824,6 +2852,13 @@ table: name: parts_order_lines schema: public + - name: tasks + using: + foreign_key_constraint_on: + column: joblineid + table: + name: tasks + schema: public insert_permissions: - role: user permission: @@ -3311,6 +3346,13 @@ table: name: scoreboard schema: public + - name: tasks + using: + foreign_key_constraint_on: + column: jobid + table: + name: tasks + schema: public - name: timetickets using: foreign_key_constraint_on: @@ -4200,13 +4242,13 @@ interval_sec: 10 num_retries: 0 timeout_sec: 60 - webhook: https://worktest.home.irony.online + webhook_from_env: HASURA_API_URL headers: - name: event-secret value_from_env: EVENT_SECRET request_transform: method: POST - query_params: { } + query_params: {} template_engine: Kriti url: '{{$base_url}}/job/statustransition' version: 2 @@ -4220,7 +4262,7 @@ webhook_from_env: HASURA_API_URL request_transform: method: POST - query_params: { } + query_params: {} template_engine: Kriti url: '{{$base_url}}/record-handler/arms' version: 2 @@ -4243,7 +4285,7 @@ value_from_env: EVENT_SECRET request_transform: method: POST - query_params: { } + query_params: {} template_engine: Kriti url: '{{$base_url}}/opensearch' version: 2 @@ -4256,13 +4298,13 @@ columns: - key - value - filter: { } + filter: {} - role: user permission: columns: - key - value - filter: { } + filter: {} - table: name: messages schema: public @@ -4694,7 +4736,7 @@ value_from_env: EVENT_SECRET request_transform: method: POST - query_params: { } + query_params: {} template_engine: Kriti url: '{{$base_url}}/opensearch' version: 2 @@ -5008,6 +5050,13 @@ table: name: parts_order_lines schema: public + - name: tasks + using: + foreign_key_constraint_on: + column: partsorderid + table: + name: tasks + schema: public insert_permissions: - role: user permission: @@ -5302,7 +5351,7 @@ value_from_env: EVENT_SECRET request_transform: method: POST - query_params: { } + query_params: {} template_engine: Kriti url: '{{$base_url}}/opensearch' version: 2 @@ -5623,6 +5672,129 @@ _eq: X-Hasura-User-Id - active: _eq: true +- table: + name: tasks + schema: public + object_relationships: + - name: bill + using: + foreign_key_constraint_on: billid + - name: bodyshop + using: + foreign_key_constraint_on: bodyshopid + - name: job + using: + foreign_key_constraint_on: jobid + - name: jobline + using: + foreign_key_constraint_on: joblineid + - name: parts_order + using: + foreign_key_constraint_on: partsorderid + - name: user + using: + foreign_key_constraint_on: assigned_to + - name: userByCreatedBy + using: + foreign_key_constraint_on: created_by + insert_permissions: + - role: user + permission: + check: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + columns: + - completed + - deleted + - priority + - assigned_to + - created_by + - description + - title + - completed_at + - created_at + - deleted_at + - due_date + - remind_at + - updated_at + - billid + - bodyshopid + - id + - jobid + - joblineid + - partsorderid + select_permissions: + - role: user + permission: + columns: + - completed + - deleted + - priority + - assigned_to + - created_by + - description + - title + - completed_at + - created_at + - deleted_at + - due_date + - remind_at + - updated_at + - billid + - bodyshopid + - id + - jobid + - joblineid + - partsorderid + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + allow_aggregations: true + update_permissions: + - role: user + permission: + columns: + - completed + - deleted + - priority + - assigned_to + - created_by + - description + - title + - completed_at + - created_at + - deleted_at + - due_date + - remind_at + - updated_at + - billid + - bodyshopid + - id + - jobid + - joblineid + - partsorderid + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + check: null - table: name: timetickets schema: public @@ -5843,7 +6015,7 @@ - user: authid: _eq: X-Hasura-User-Id - check: { } + check: {} - table: name: tt_approval_queue schema: public @@ -6006,6 +6178,13 @@ table: name: exportlog schema: public + - name: ioevents + using: + foreign_key_constraint_on: + column: useremail + table: + name: ioevents + schema: public - name: messages using: foreign_key_constraint_on: @@ -6034,6 +6213,20 @@ table: name: parts_orders schema: public + - name: tasks + using: + foreign_key_constraint_on: + column: assigned_to + table: + name: tasks + schema: public + - name: tasksByCreatedBy + using: + foreign_key_constraint_on: + column: created_by + table: + name: tasks + schema: public - name: timetickets using: foreign_key_constraint_on: @@ -6051,7 +6244,7 @@ insert_permissions: - role: user permission: - check: { } + check: {} columns: - authid - email @@ -6253,7 +6446,7 @@ value_from_env: EVENT_SECRET request_transform: method: POST - query_params: { } + query_params: {} template_engine: Kriti url: '{{$base_url}}/opensearch' version: 2 From ab2323e5c1202c3c3a57090611c690f2cca471bb Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Wed, 20 Mar 2024 22:19:52 -0400 Subject: [PATCH 004/134] - Progress Commit Signed-off-by: Dave Richer --- .../components/header/header.component.jsx | 60 +++++++--- .../task-list/task-list.component.jsx | 92 ++++++++++++--- .../task-list/task-list.container.jsx | 49 +++++++- .../task-upsert-modal.component.jsx | 107 ++++++++++++++++++ .../task-upsert-modal.container.jsx | 105 +++++++++++++++++ client/src/graphql/bodyshop.queries.js | 19 ---- client/src/graphql/tasks.queries.js | 70 +++++++++++- .../pages/manage/manage.page.component.jsx | 8 +- client/src/redux/modals/modals.reducer.js | 1 + client/src/redux/modals/modals.selectors.js | 5 + client/src/redux/modals/modals.types.js | 2 +- 11 files changed, 454 insertions(+), 64 deletions(-) create mode 100644 client/src/components/task-upsert-modal/task-upsert-modal.component.jsx create mode 100644 client/src/components/task-upsert-modal/task-upsert-modal.container.jsx diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx index 0dfac2ac6..75a9e39c0 100644 --- a/client/src/components/header/header.component.jsx +++ b/client/src/components/header/header.component.jsx @@ -17,6 +17,7 @@ import Icon, { LineChartOutlined, PaperClipOutlined, PhoneOutlined, + PlusCircleOutlined, QuestionCircleFilled, ScheduleOutlined, SettingOutlined, @@ -25,11 +26,11 @@ import Icon, { UnorderedListOutlined, UserOutlined, } from '@ant-design/icons'; -import { useSplitTreatments } from '@splitsoftware/splitio-react'; -import { Layout, Menu, Switch, Tooltip } from 'antd'; -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { BsKanban } from 'react-icons/bs'; +import {useSplitTreatments} from '@splitsoftware/splitio-react'; +import {Layout, Menu, Switch, Tooltip} from 'antd'; +import React, {useEffect, useState} from 'react'; +import {useTranslation} from 'react-i18next'; +import {BsKanban} from 'react-icons/bs'; import { FaCalendarAlt, FaCarCrash, @@ -37,23 +38,23 @@ import { FaFileInvoiceDollar, FaTasks } from 'react-icons/fa'; -import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from 'react-icons/gi'; -import { IoBusinessOutline } from 'react-icons/io5'; -import { RiSurveyLine } from 'react-icons/ri'; -import { connect } from 'react-redux'; -import { Link } from 'react-router-dom'; -import { createStructuredSelector } from 'reselect'; +import {GiPayMoney, GiPlayerTime, GiSettingsKnobs} from 'react-icons/gi'; +import {IoBusinessOutline} from 'react-icons/io5'; +import {RiSurveyLine} from 'react-icons/ri'; +import {connect} from 'react-redux'; +import {Link} from 'react-router-dom'; +import {createStructuredSelector} from 'reselect'; import { selectRecentItems, selectSelectedHeader, } from '../../redux/application/application.selectors'; -import { setModalContext } from '../../redux/modals/modals.actions'; -import { signOutStart } from '../../redux/user/user.actions'; -import { selectBodyshop, selectCurrentUser } from '../../redux/user/user.selectors'; -import { FiLogOut } from 'react-icons/fi'; -import { checkBeta, handleBeta, setBeta } from '../../utils/betaHandler'; +import {setModalContext} from '../../redux/modals/modals.actions'; +import {signOutStart} from '../../redux/user/user.actions'; +import {selectBodyshop, selectCurrentUser} from '../../redux/user/user.selectors'; +import {FiLogOut} from 'react-icons/fi'; +import {checkBeta, handleBeta, setBeta} from '../../utils/betaHandler'; import InstanceRenderManager from '../../utils/instanceRenderMgr'; -import { HasFeatureAccess } from '../feature-wrapper/feature-wrapper.component'; +import {HasFeatureAccess} from '../feature-wrapper/feature-wrapper.component'; const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser, @@ -73,6 +74,10 @@ const mapDispatchToProps = (dispatch) => ({ signOutStart: () => dispatch(signOutStart()), setCardPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: 'cardPayment' })), + setTaskUpsertContext: (context) => dispatch(setModalContext({ + context: context, + modal: 'taskUpsert' + })), }); function Header({ @@ -87,6 +92,7 @@ function Header({ setReportCenterContext, recentItems, setCardPaymentContext, + setTaskUpsertContext, }) { const { treatments: { ImEXPay, DmsAp, Simple_Inventory }, @@ -470,7 +476,25 @@ function Header({ key: 'tasks', id: 'tasks', icon: , - label: {t('menus.header.tasks')}, + label: t('menus.header.tasks'), + children: [ + { + key: 'createTask', + icon: , + label: t('menus.header.create_task'), + onClick: () => { + setTaskUpsertContext({ + actions: {}, + context: {}, + }); + }, + }, + { + key: 'mytasks', + icon: , + label: {t('menus.header.tasks')}, + } + ] }, { key: 'shopsubmenu', diff --git a/client/src/components/task-list/task-list.component.jsx b/client/src/components/task-list/task-list.component.jsx index 4a9aea817..2f1d125b8 100644 --- a/client/src/components/task-list/task-list.component.jsx +++ b/client/src/components/task-list/task-list.component.jsx @@ -2,58 +2,91 @@ import {Button, Card, Space, Switch, Table} from "antd"; import queryString from "query-string"; import React, {useCallback, useState} from "react"; import {useTranslation} from "react-i18next"; -import {useLocation, useNavigate} from "react-router-dom"; +import {Link, useLocation, useNavigate} from "react-router-dom"; import {pageLimit} from "../../utils/config"; import dayjs from "../../utils/day"; import { CheckCircleFilled, CheckCircleOutlined, DeleteFilled, - DeleteOutlined, ExclamationCircleFilled, + DeleteOutlined, + EditFilled, + ExclamationCircleFilled, + PlusCircleFilled, SyncOutlined } from "@ant-design/icons"; +import {DateFormatter} from "../../utils/DateFormatter.jsx"; +import {connect} from 'react-redux'; +import {setModalContext} from '../../redux/modals/modals.actions'; +/** + * Task List Component + * @param dueDate + * @returns {Element} + * @constructor + */ const DueDateRecord = ({dueDate}) => { if (dueDate) { - const dueDateFormatted = dayjs(dueDate).format("YYYY-MM-DD"); - const relativeDueDate = dayjs(dueDate).fromNow(); - const today = dayjs().format("YYYY-MM-DD"); - if (dueDateFormatted < today) { - return
{dueDateFormatted}
; + const dueDateDayjs = dayjs(dueDate); + const relativeDueDate = dueDateDayjs.fromNow(); + const today = dayjs(); + + if (dueDateDayjs.isAfter(today)) { + return
+ {dueDate}
; } else { - return
{dueDateFormatted}
; + return
{dueDate}
; } } else { return
N/A
; } } + +/** + * Priority Label Component + * @param priority + * @returns {Element} + * @constructor + */ const PriorityLabel = ({priority}) => { switch(priority) { case 1: return
- High + High
; case 2: return
- Medium + Medium
; case 3: return
- Low + Low
; default: return
- None + None
; } } -export default function TaskListComponent({ +const mapDispatchToProps = (dispatch) => ({ + // Existing dispatch props... + setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})), +}); + +const mapStateToProps = (state) => ({ + // Existing state props... +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TaskListComponent); + +function TaskListComponent({ loading, tasks, total, refetch, toggleCompletedStatus, + setTaskUpsertContext, }) { const {t} = useTranslation(); @@ -71,6 +104,17 @@ export default function TaskListComponent({ const history = useNavigate(); const columns = [ + { + title: t("tasks.fields.job.ro_number"), + dataIndex: ["job", "ro_number"], + key: "job.ro_number", + width: '5%', + render: (text, record) => ( + + {record.job.ro_number} + + ), + }, { title: t("tasks.fields.title"), dataIndex: "title", @@ -110,6 +154,16 @@ export default function TaskListComponent({ width: '5%', render: (text, record) => ( +
{ + const handleTaskUpdated = () => { + refetch().catch((e) => { + console.error(`Something went wrong fetching tasks: ${e.message || ''}`); + }); }; + + window.addEventListener('taskUpdated', handleTaskUpdated); + window.addEventListener('taskCreated', handleTaskUpdated); + + return () => { + window.removeEventListener('taskUpdated', handleTaskUpdated); + window.removeEventListener('taskCreated', handleTaskUpdated); + }; + }, [refetch]); + + /** + * Toggle task completed mutation + */ const [toggleTaskCompleted] = useMutation(MUTATION_TOGGLE_TASK_COMPLETED); + /** + * Toggle task completed status + * @param id + * @param currentStatus + * @returns {Promise} + */ const toggleCompletedStatus = async (id, currentStatus) => { const completed_at = !currentStatus ? new Date().toISOString() : null; await toggleTaskCompleted({ @@ -49,11 +76,21 @@ export default function TaskListContainer({bodyshop, currentUser}) { completed_at: completed_at } }); - refetch(); - }; + refetch().catch((e) => { + console.error(`Something went wrong fetching tasks: ${e.message || ''}`); + }); }; + /** + * Toggle task deleted mutation + */ const [toggleTaskDeleted] = useMutation(MUTATION_TOGGLE_TASK_DELETED); + /** + * Toggle task deleted status + * @param id + * @param currentStatus + * @returns {Promise} + */ const toggleDeletedStatus = async (id, currentStatus) => { const deleted_at = !currentStatus ? new Date().toISOString() : null; await toggleTaskDeleted({ @@ -63,7 +100,9 @@ export default function TaskListContainer({bodyshop, currentUser}) { deleted_at: deleted_at } }); - refetch(); + refetch().catch((e) => { + console.error(`Something went wrong fetching tasks: ${e.message || ''}`); + }); }; if (error) return ; diff --git a/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx b/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx new file mode 100644 index 000000000..b1661a6a0 --- /dev/null +++ b/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx @@ -0,0 +1,107 @@ +import {Col, Form, Input, Row, Select, Switch} from "antd"; +import React from "react"; +import {useTranslation} from "react-i18next"; +import {FormDatePicker} from "../form-date-picker/form-date-picker.component.jsx"; +import {createStructuredSelector} from "reselect"; +import {selectBodyshop} from "../../redux/user/user.selectors.js"; +import {connect} from "react-redux"; + +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, +}); + +const mapDispatchToProps = (dispatch) => ({}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(TaskUpsertModalComponent); + + +export function TaskUpsertModalComponent({form, bodyshop}) { + const {t} = useTranslation(); + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx new file mode 100644 index 000000000..f8bfcbe28 --- /dev/null +++ b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx @@ -0,0 +1,105 @@ +import {useMutation} from "@apollo/client"; +import {Form, Modal, notification} from "antd"; +import React, {useEffect} from "react"; +import {useTranslation} from "react-i18next"; +import {connect} from "react-redux"; +import {createStructuredSelector} from "reselect"; +import {INSERT_NEW_TASK, UPDATE_TASK} from "../../graphql/tasks.queries"; +import {toggleModalVisible} from "../../redux/modals/modals.actions"; +import {selectTaskUpsert} from "../../redux/modals/modals.selectors"; +import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors"; +import TaskUpsertModalComponent from "./task-upsert-modal.component"; + +const mapStateToProps = createStructuredSelector({ + currentUser: selectCurrentUser, + bodyshop: selectBodyshop, + taskUpsert: selectTaskUpsert, +}); +const mapDispatchToProps = (dispatch) => ({ + toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")), +}); + +export function TaskUpsertModalContainer({ + bodyshop, + currentUser, + taskUpsert, + toggleModalVisible, + }) { + const {t} = useTranslation(); + const [insertTask] = useMutation(INSERT_NEW_TASK); + const [updateTask] = useMutation(UPDATE_TASK); + + const {open, context, actions} = taskUpsert; + const {jobId, existingTask} = context; + const {refetch} = actions; + + const [form] = Form.useForm(); + + useEffect(() => { + if (existingTask && open) { + form.setFieldsValue(existingTask); + } else if (!existingTask && open) { + form.resetFields(); + } + }, [existingTask, form, open]); + + const handleFinish = async (formValues) => { + const {...values} = formValues; + + if (existingTask) { + updateTask({ + variables: { + taskId: existingTask.id, + task: values, + }, + }).then((r) => { + window.dispatchEvent(new CustomEvent('taskUpdated')); + notification["success"]({ + message: t("tasks.successes.updated"), + }); + }); + if (refetch) refetch(); + toggleModalVisible(); + } else { + await insertTask({ + variables: { + taskInput: [ + {...values, jobid: jobId, created_by: currentUser.email, bodyshopid: bodyshop.id}, + ], + }, + }); + + if (refetch) refetch(); + form.resetFields(); + toggleModalVisible(); + window.dispatchEvent(new CustomEvent('taskUpdated')); + notification["success"]({ + message: t("tasks.successes.create"), + }); + } + }; + + return ( + { + form.submit(); + }} + onCancel={() => { + toggleModalVisible(); + }} + destroyOnClose + > +
+ + +
+ ); +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(TaskUpsertModalContainer); diff --git a/client/src/graphql/bodyshop.queries.js b/client/src/graphql/bodyshop.queries.js index 5de9d7521..a8efb8d0b 100644 --- a/client/src/graphql/bodyshop.queries.js +++ b/client/src/graphql/bodyshop.queries.js @@ -160,25 +160,6 @@ export const QUERY_BODYSHOP = gql` external_id flat_rate } - tasks { - id - created_at - updated_at - title - description - deleted - deleted_at - due_date - created_by - assigned_to - completed - completed_at - remind_at - priority - jobid - joblineid - partsorderid - } } } `; diff --git a/client/src/graphql/tasks.queries.js b/client/src/graphql/tasks.queries.js index 0a4375b1d..c55987a83 100644 --- a/client/src/graphql/tasks.queries.js +++ b/client/src/graphql/tasks.queries.js @@ -81,10 +81,10 @@ export const QUERY_MY_TASKS_PAGINATED = gql` completed_at remind_at priority - jobid - joblineid - partsorderid - billid + job { + ro_number + id + } bodyshopid } tasks_aggregate( @@ -129,3 +129,65 @@ export const MUTATION_TOGGLE_TASK_DELETED = gql` } } `; + +/** + * Insert new task mutation + * @type {DocumentNode} + */ +export const INSERT_NEW_TASK = gql` + mutation INSERT_NEW_TASK($taskInput: [tasks_insert_input!]!) { + insert_tasks(objects: $taskInput) { + returning { + id + created_at + updated_at + title + description + deleted + deleted_at + due_date + created_by + assigned_to + completed + completed_at + remind_at + priority + jobid + joblineid + partsorderid + billid + } + } + } +`; + +/** + * Update task mutation + * @type {DocumentNode} + */ +export const UPDATE_TASK = gql` + mutation UPDATE_TASK($taskId: uuid!, $task: tasks_set_input!) { + update_tasks(where: { id: { _eq: $taskId } }, _set: $task) { + returning { + id + created_at + updated_at + title + description + deleted + deleted_at + due_date + created_by + assigned_to + completed + completed_at + remind_at + priority + jobid + joblineid + partsorderid + billid + } + } + } +` diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx index e29f462ff..c7a717e36 100644 --- a/client/src/pages/manage/manage.page.component.jsx +++ b/client/src/pages/manage/manage.page.component.jsx @@ -35,7 +35,11 @@ import "./manage.page.styles.scss"; const JobsPage = lazy(() => import("../jobs/jobs.page")); const CardPaymentModalContainer = lazy(() => - import("../../components/card-payment-modal/card-payment-modal.container.") + import("../../components/card-payment-modal/card-payment-modal.container..jsx") +); + +const TaskUpsertModalContainer = lazy(() => + import("../../components/task-upsert-modal/task-upsert-modal.container.jsx") ); const JobsDetailPage = lazy(() => @@ -229,7 +233,7 @@ export function Manage({conflict, bodyshop,enableJoyRide,joyRideSteps,setJoyRide - + diff --git a/client/src/redux/modals/modals.reducer.js b/client/src/redux/modals/modals.reducer.js index 5cd7e5763..b7d8c2cee 100644 --- a/client/src/redux/modals/modals.reducer.js +++ b/client/src/redux/modals/modals.reducer.js @@ -13,6 +13,7 @@ const INITIAL_STATE = { billEnter: {...baseModal}, courtesyCarReturn: {...baseModal}, noteUpsert: {...baseModal}, + taskUpsert: {...baseModal}, schedule: {...baseModal}, partsOrder: {...baseModal}, timeTicket: {...baseModal}, diff --git a/client/src/redux/modals/modals.selectors.js b/client/src/redux/modals/modals.selectors.js index a2f7d5ade..6d2f383f7 100644 --- a/client/src/redux/modals/modals.selectors.js +++ b/client/src/redux/modals/modals.selectors.js @@ -22,6 +22,11 @@ export const selectNoteUpsert = createSelector( (modals) => modals.noteUpsert ); +export const selectTaskUpsert = createSelector( + [selectModals], + (modals) => modals.taskUpsert +); + export const selectSchedule = createSelector( [selectModals], (modals) => modals.schedule diff --git a/client/src/redux/modals/modals.types.js b/client/src/redux/modals/modals.types.js index 911f4c581..6bc9a7a9e 100644 --- a/client/src/redux/modals/modals.types.js +++ b/client/src/redux/modals/modals.types.js @@ -1,5 +1,5 @@ const ModalActionTypes = { TOGGLE_MODAL_VISIBLE: "TOGGLE_MODAL_VISIBLE", - SET_MODAL_CONTEXT: "SET_MODAL_CONTEXT" + SET_MODAL_CONTEXT: "SET_MODAL_CONTEXT", }; export default ModalActionTypes; From 9012e4deec513c848a134801cc5d63fe0d262b26 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Thu, 21 Mar 2024 13:42:08 -0400 Subject: [PATCH 005/134] - Progress Commit Signed-off-by: Dave Richer --- .../components/header/header.component.jsx | 2 +- .../task-upsert-modal.container.jsx | 24 ++++++++++++++----- client/src/graphql/tasks.queries.js | 8 +++---- client/src/redux/modals/modals.reducer.js | 2 +- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx index 75a9e39c0..1b5d5b5c4 100644 --- a/client/src/components/header/header.component.jsx +++ b/client/src/components/header/header.component.jsx @@ -476,7 +476,7 @@ function Header({ key: 'tasks', id: 'tasks', icon: , - label: t('menus.header.tasks'), + label: t('menus.header.my_tasks'), children: [ { key: 'createTask', diff --git a/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx index f8bfcbe28..d091b595c 100644 --- a/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx +++ b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx @@ -4,7 +4,7 @@ import React, {useEffect} from "react"; import {useTranslation} from "react-i18next"; import {connect} from "react-redux"; import {createStructuredSelector} from "reselect"; -import {INSERT_NEW_TASK, UPDATE_TASK} from "../../graphql/tasks.queries"; +import {MUTATION_INSERT_NEW_TASK, MUTATION_UPDATE_TASK} from "../../graphql/tasks.queries"; import {toggleModalVisible} from "../../redux/modals/modals.actions"; import {selectTaskUpsert} from "../../redux/modals/modals.selectors"; import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors"; @@ -26,13 +26,12 @@ export function TaskUpsertModalContainer({ toggleModalVisible, }) { const {t} = useTranslation(); - const [insertTask] = useMutation(INSERT_NEW_TASK); - const [updateTask] = useMutation(UPDATE_TASK); + const [insertTask] = useMutation(MUTATION_INSERT_NEW_TASK); + const [updateTask] = useMutation(MUTATION_UPDATE_TASK); const {open, context, actions} = taskUpsert; const {jobId, existingTask} = context; const {refetch} = actions; - const [form] = Form.useForm(); useEffect(() => { @@ -53,7 +52,6 @@ export function TaskUpsertModalContainer({ task: values, }, }).then((r) => { - window.dispatchEvent(new CustomEvent('taskUpdated')); notification["success"]({ message: t("tasks.successes.updated"), }); @@ -67,12 +65,26 @@ export function TaskUpsertModalContainer({ {...values, jobid: jobId, created_by: currentUser.email, bodyshopid: bodyshop.id}, ], }, + update(cache) { + cache.modify({ + + fields: { + tasks(existingTasks) { + return [{ + ...values, + jobid: jobId, + created_by: currentUser.email, + bodyshopid: bodyshop.id + }, ...existingTasks] + }, + }, + }); + }, }); if (refetch) refetch(); form.resetFields(); toggleModalVisible(); - window.dispatchEvent(new CustomEvent('taskUpdated')); notification["success"]({ message: t("tasks.successes.create"), }); diff --git a/client/src/graphql/tasks.queries.js b/client/src/graphql/tasks.queries.js index c55987a83..9a501f432 100644 --- a/client/src/graphql/tasks.queries.js +++ b/client/src/graphql/tasks.queries.js @@ -134,8 +134,8 @@ export const MUTATION_TOGGLE_TASK_DELETED = gql` * Insert new task mutation * @type {DocumentNode} */ -export const INSERT_NEW_TASK = gql` - mutation INSERT_NEW_TASK($taskInput: [tasks_insert_input!]!) { +export const MUTATION_INSERT_NEW_TASK = gql` + mutation MUTATION_INSERT_NEW_TASK($taskInput: [tasks_insert_input!]!) { insert_tasks(objects: $taskInput) { returning { id @@ -165,8 +165,8 @@ export const INSERT_NEW_TASK = gql` * Update task mutation * @type {DocumentNode} */ -export const UPDATE_TASK = gql` - mutation UPDATE_TASK($taskId: uuid!, $task: tasks_set_input!) { +export const MUTATION_UPDATE_TASK = gql` + mutation MUTATION_UPDATE_TASK($taskId: uuid!, $task: tasks_set_input!) { update_tasks(where: { id: { _eq: $taskId } }, _set: $task) { returning { id diff --git a/client/src/redux/modals/modals.reducer.js b/client/src/redux/modals/modals.reducer.js index b7d8c2cee..6260fe39e 100644 --- a/client/src/redux/modals/modals.reducer.js +++ b/client/src/redux/modals/modals.reducer.js @@ -13,7 +13,7 @@ const INITIAL_STATE = { billEnter: {...baseModal}, courtesyCarReturn: {...baseModal}, noteUpsert: {...baseModal}, - taskUpsert: {...baseModal}, + taskUpsert: {...baseModal, }, schedule: {...baseModal}, partsOrder: {...baseModal}, timeTicket: {...baseModal}, From 40c801592d1a853ce7a6d836457aaf0ffaa1d908 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Fri, 22 Mar 2024 12:20:56 -0400 Subject: [PATCH 006/134] - Progress Commit Signed-off-by: Dave Richer --- .../task-upsert-modal.component.jsx | 30 +++++++++++++++++++ client/src/graphql/tasks.queries.js | 25 ++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx b/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx index b1661a6a0..44262b68d 100644 --- a/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx +++ b/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx @@ -101,6 +101,36 @@ export function TaskUpsertModalComponent({form, bodyshop}) { + + + + + + + + + + + + + + + ); diff --git a/client/src/graphql/tasks.queries.js b/client/src/graphql/tasks.queries.js index 9a501f432..d19b70315 100644 --- a/client/src/graphql/tasks.queries.js +++ b/client/src/graphql/tasks.queries.js @@ -82,10 +82,31 @@ export const QUERY_MY_TASKS_PAGINATED = gql` remind_at priority job { - ro_number id + ro_number + joblines { + id + line_desc + } + bills { + id + vendor { + name + } + invoice_number + } + parts_orders { + id + vendor { + name + } + order_number + } } - bodyshopid + jobid + joblineid + partsorderid + billid } tasks_aggregate( where: { From 815ada0516ac38937f0ed190cee2aedf2048af36 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 25 Mar 2024 15:38:38 -0700 Subject: [PATCH 007/134] WIP RO Closer. --- bodyshop_translations.babel | 170 +++++++++++++++++- .../job-bills-total.component.jsx | 51 ++++-- .../job-close-ro-gaurd.labor.jsx | 6 +- .../job-close-ro-guard.ar.jsx | 56 ++++++ .../job-close-ro-guard.bills.jsx | 4 +- .../job-close-ro-guard.container.jsx | 96 +++++++--- .../job-close-ro-guard.ppd.jsx | 85 +++++++++ .../job-close-ro-guard.profit.jsx | 28 +-- .../job-close-ro-guard.styles.scss | 5 + .../job-close-ro-guard.sublet.jsx | 89 +++++++++ .../job-close-ro-guard.tt-lifecycle.jsx | 2 +- .../job-costing-statistics.component.jsx | 23 ++- .../job-lifecycle/job-lifecycle.component.jsx | 4 +- .../job-payments/job-payments.component.jsx | 1 + .../labor-allocations-table.component.jsx | 19 +- ...or-allocations-table.payroll.component.jsx | 16 +- client/src/graphql/jobs.queries.js | 17 ++ client/src/translations/en_us/common.json | 7 + client/src/translations/es/common.json | 8 + client/src/translations/fr/common.json | 8 + 20 files changed, 635 insertions(+), 60 deletions(-) create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-guard.ar.jsx create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-guard.ppd.jsx create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-guard.styles.scss create mode 100644 client/src/components/job-close-ro-guard/job-close-ro-guard.sublet.jsx diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index ee0d31476..73e9c07c0 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -1,4 +1,4 @@ - + +
+ +
+ + +
${strings.header}

${strings.subHeader}

+ + + + +
+ +
${strings.body}
+ + + + + + + + + + +
 
+ + + + +` +} + +module.exports = generateEmailTemplate; diff --git a/server/email/sendemail.js b/server/email/sendemail.js index cface107f..a976cee7d 100644 --- a/server/email/sendemail.js +++ b/server/email/sendemail.js @@ -10,6 +10,8 @@ const InstanceManager = require("../utils/instanceMgr").default; const logger = require("../utils/logger"); const client = require("../graphql-client/graphql-client").client; const queries = require("../graphql-client/queries"); +const {isObject} = require("lodash"); +const generateEmailTemplate = require('./generateTemplate'); const ses = new aws.SES({ // The key apiVersion is no longer supported in v3, and can be removed. @@ -88,7 +90,8 @@ exports.sendEmail = async (req, res) => { replyTo: req.body.ReplyTo.Email, to: req.body.to, cc: req.body.cc, - subject: req.body.subject + subject: req.body.subject, + templateStrings: req.body.templateStrings }); let downloadedMedia = []; @@ -104,6 +107,7 @@ exports.sendEmail = async (req, res) => { to: req.body.to, cc: req.body.cc, subject: req.body.subject, + templateStrings: req.body.templateStrings, error }); } @@ -134,7 +138,7 @@ exports.sendEmail = async (req, res) => { }; }) ] || null, - html: req.body.html, + html: isObject(req.body?.templateStrings) ? generateEmailTemplate(req.body.templateStrings) : req.body.html, ses: { // optional extra arguments for SendRawEmail Tags: [ @@ -153,7 +157,8 @@ exports.sendEmail = async (req, res) => { replyTo: req.body.ReplyTo.Email, to: req.body.to, cc: req.body.cc, - subject: req.body.subject + subject: req.body.subject, + templateStrings: req.body.templateStrings // info, }); logEmail(req, { @@ -172,6 +177,7 @@ exports.sendEmail = async (req, res) => { to: req.body.to, cc: req.body.cc, subject: req.body.subject, + templateStrings: req.body.templateStrings, error: err }); logEmail(req, { From 2b172f99997edeffabae740c4a5e40e18b64e9e1 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Tue, 2 Apr 2024 21:54:07 -0400 Subject: [PATCH 025/134] - fix Signed-off-by: Dave Richer --- .../task-upsert-modal/task-upsert-modal.container.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx index cb7f03cf3..cd8cd5aee 100644 --- a/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx +++ b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx @@ -18,7 +18,6 @@ import {replaceUndefinedWithNull} from "../../utils/undefinedtonull.js"; import {useNavigate} from "react-router-dom"; import axios from "axios"; import dayjs from '../../utils/day'; -import {insertAuditTrail} from "../../redux/application/application.actions.js"; const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser, @@ -27,7 +26,6 @@ const mapStateToProps = createStructuredSelector({ }); const mapDispatchToProps = (dispatch) => ({ toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")), - insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type })) }); export function TaskUpsertModalContainer({ @@ -35,7 +33,6 @@ export function TaskUpsertModalContainer({ currentUser, taskUpsert, toggleModalVisible, - insertAuditTrail }) { const {t} = useTranslation(); const history = useNavigate(); From 5c0f228876e882e4c79bfb93834834bc616a4483 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 3 Apr 2024 09:27:29 -0700 Subject: [PATCH 026/134] Remove debug statement. --- server/job/job-totals-USA.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/job/job-totals-USA.js b/server/job/job-totals-USA.js index 700ea451d..7c8437e26 100644 --- a/server/job/job-totals-USA.js +++ b/server/job/job-totals-USA.js @@ -924,7 +924,6 @@ function CalculateTaxesTotals(job, otherTotals) { if ( thresholdAmount === 9999.99 || InstanceMgr({ - debug: true, imex: false, rome: false, promanager: thresholdAmount === 0 && parseInt(tyCounter) === 1 From 37196e65c3ec98219cf4d85b2a4980ae2231badd Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 3 Apr 2024 09:46:52 -0700 Subject: [PATCH 027/134] Add schema changes for RO Guard to bodyshop table. --- hasura/metadata/tables.yaml | 2 ++ .../down.sql | 4 ++++ .../up.sql | 2 ++ 3 files changed, 8 insertions(+) create mode 100644 hasura/migrations/1712162752434_alter_table_public_bodyshops_add_column_md_ro_guard/down.sql create mode 100644 hasura/migrations/1712162752434_alter_table_public_bodyshops_add_column_md_ro_guard/up.sql diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 38dff43fa..7a9de6b10 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -969,6 +969,7 @@ - md_rbac - md_referral_sources - md_responsibility_centers + - md_ro_guard - md_ro_statuses - md_tasks_presets - md_to_emails @@ -1068,6 +1069,7 @@ - md_rbac - md_referral_sources - md_responsibility_centers + - md_ro_guard - md_ro_statuses - md_tasks_presets - md_to_emails diff --git a/hasura/migrations/1712162752434_alter_table_public_bodyshops_add_column_md_ro_guard/down.sql b/hasura/migrations/1712162752434_alter_table_public_bodyshops_add_column_md_ro_guard/down.sql new file mode 100644 index 000000000..8f554a895 --- /dev/null +++ b/hasura/migrations/1712162752434_alter_table_public_bodyshops_add_column_md_ro_guard/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."bodyshops" add column "md_ro_guard" jsonb +-- null default jsonb_build_object(); diff --git a/hasura/migrations/1712162752434_alter_table_public_bodyshops_add_column_md_ro_guard/up.sql b/hasura/migrations/1712162752434_alter_table_public_bodyshops_add_column_md_ro_guard/up.sql new file mode 100644 index 000000000..a932a2446 --- /dev/null +++ b/hasura/migrations/1712162752434_alter_table_public_bodyshops_add_column_md_ro_guard/up.sql @@ -0,0 +1,2 @@ +alter table "public"."bodyshops" add column "md_ro_guard" jsonb + null default jsonb_build_object(); From 3eb010285da4b843813e11151cbafc4226e129b2 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Wed, 3 Apr 2024 13:01:56 -0400 Subject: [PATCH 028/134] - Update date picker presets Signed-off-by: Dave Richer --- .../task-upsert-modal/task-upsert-modal.component.jsx | 4 ++++ client/src/translations/en_us/common.json | 5 ++++- client/src/translations/es/common.json | 5 ++++- client/src/translations/fr/common.json | 5 ++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx b/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx index 642e18299..853c89825 100644 --- a/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx +++ b/client/src/components/task-upsert-modal/task-upsert-modal.component.jsx @@ -36,10 +36,14 @@ export function TaskUpsertModalComponent({ const {t} = useTranslation(); const datePickerPresets = [ + {label: t('tasks.date_presets.today'), value: dayjs()}, + {label: t('tasks.date_presets.tomorrow'), value: dayjs().add(1, 'day')}, {label: t('tasks.date_presets.next_week'), value: dayjs().add(1, 'week')}, {label: t('tasks.date_presets.two_weeks'), value: dayjs().add(2, 'weeks')}, {label: t('tasks.date_presets.three_weeks'), value: dayjs().add(3, 'weeks')}, {label: t('tasks.date_presets.one_month'), value: dayjs().add(1, 'month')}, + {label: t('tasks.date_presets.three_months'), value: dayjs().add(3, 'month')}, + ]; const clearRelations = () => { diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 960479972..db650382e 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -2116,7 +2116,10 @@ "next_week": "Next Week", "two_weeks": "Two Weeks", "three_weeks": "Three Weeks", - "one_month": "One Month" + "one_month": "One Month", + "today": "Today", + "tomorrow": "Tomorrow", + "three_months": "Three Months" }, "actions": { "new": "New Task", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 5221925dc..c442aeebc 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2116,7 +2116,10 @@ "next_week": "", "two_weeks": "", "three_weeks": "", - "one_month": "" + "one_month": "", + "today": "", + "tomorrow": "", + "three_months": "" }, "actions": { "new": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 366a33369..019790f3b 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -2116,7 +2116,10 @@ "next_week": "", "two_weeks": "", "three_weeks": "", - "one_month": "" + "one_month": "", + "today": "", + "tomorrow": "", + "three_months": "" }, "actions": { "new": "", From e51f72ff983f4992add335196ed1ed2a5bf0affd Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Wed, 3 Apr 2024 13:47:40 -0400 Subject: [PATCH 029/134] - its sign, not sing :D Signed-off-by: Dave Richer --- .../task-upsert-modal/task-upsert-modal.container.jsx | 4 ++-- server/email/generateTemplate.js | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx index cd8cd5aee..5d13901c8 100644 --- a/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx +++ b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx @@ -144,7 +144,7 @@ export function TaskUpsertModalContainer({ templateStrings: { header: values.title, subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format('MM/DD/YYYY')}` : ''}`, - body: `Please sing in to your account to view the task details.` + body: `Please sign in to your account to view the task details.` } }).catch(e => console.error(`Something went wrong sending email to Assigned party on Task creation. ${e.message || ''}`)); } @@ -211,7 +211,7 @@ export function TaskUpsertModalContainer({ templateStrings: { header: values.title, subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format('MM/DD/YYYY')}` : ''}`, - body: `Please sing in to your account to view the task details.` + body: `Please sign to your account to view the task details.` } }).catch(e => console.error(`Something went wrong sending email to Assigned party on Task edit. ${e.message || ''}`)); diff --git a/server/email/generateTemplate.js b/server/email/generateTemplate.js index eac8a4a55..28a3f7d2e 100644 --- a/server/email/generateTemplate.js +++ b/server/email/generateTemplate.js @@ -9,6 +9,12 @@ const moment = require("moment"); // - footer - The footer of the email // - dateLine - The date line of the email +// Google cloud scheduler / Hasura Cron Jobs (Preferred) +// Reminders every x mins +// Event Trigger + +// TODO use InstanceDynamicManager on the footer default to prevent the word IMEX from being hardcoded + const generateEmailTemplate = (strings) => { let now = () =>moment().format('MM/DD/YYYY @ hh:mm a'); From ab031c01ded6a76795b60ab4c5c9353c9593ba3f Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Wed, 3 Apr 2024 14:09:09 -0400 Subject: [PATCH 030/134] - reapply proper prettier formatting. Signed-off-by: Dave Richer --- .../bills-list-table.component.jsx | 43 +- .../components/header/header.component.jsx | 329 +- .../job-lines-expander.component.jsx | 145 +- .../job-detail-lines/job-lines.component.jsx | 4 +- .../job-line-convert-to-labor.component.jsx | 2 +- .../jobs-detail-header-actions.component.jsx | 302 +- .../jobs-list-paginated.component.jsx | 60 +- .../jobs-list/jobs-list.component.jsx | 91 +- .../parts-order-list-table.component.jsx | 98 +- .../parts-order-modal.container.jsx | 2 +- .../task-list/task-list.component.jsx | 30 +- .../task-list/task-list.container.jsx | 23 +- .../task-upsert-modal.container.jsx | 11 +- client/src/graphql/bills.queries.js | 2 +- client/src/graphql/jobs.queries.js | 14 +- client/src/graphql/tasks.queries.js | 46 +- .../jobs-detail.page.component.jsx | 20 +- .../pages/manage/manage.page.component.jsx | 261 +- .../src/pages/tasks/allTasksPageContainer.jsx | 3 +- .../src/pages/tasks/myTasksPageContainer.jsx | 3 +- client/src/pages/tasks/taskPageTypes.jsx | 4 +- client/src/translations/en_us/common.json | 6608 ++++++++-------- client/src/translations/es/common.json | 6620 ++++++++--------- client/src/translations/fr/common.json | 6616 ++++++++-------- server/email/generateTemplate.js | 4 +- server/email/sendemail.js | 34 +- 26 files changed, 10734 insertions(+), 10641 deletions(-) diff --git a/client/src/components/bills-list-table/bills-list-table.component.jsx b/client/src/components/bills-list-table/bills-list-table.component.jsx index b7ac48060..cb88f46b8 100644 --- a/client/src/components/bills-list-table/bills-list-table.component.jsx +++ b/client/src/components/bills-list-table/bills-list-table.component.jsx @@ -6,7 +6,7 @@ import {connect} from "react-redux"; import {createStructuredSelector} from "reselect"; import {selectJobReadOnly} from "../../redux/application/application.selectors"; import {setModalContext} from "../../redux/modals/modals.actions"; -import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors"; +import {selectBodyshop} from "../../redux/user/user.selectors"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; import {DateFormatter} from "../../utils/DateFormatter"; import {alphaSort, dateSort} from "../../utils/sorters"; @@ -22,7 +22,10 @@ const mapStateToProps = createStructuredSelector({ }); const mapDispatchToProps = (dispatch) => ({ - setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })), + setBillEnterContext: (context) => dispatch(setModalContext({ + context: context, + modal: "billEnter" + })), setReconciliationContext: (context) => dispatch(setModalContext({ context: context, modal: "reconciliation" @@ -40,7 +43,7 @@ export function BillsListTableComponent({ setReconciliationContext, setTaskUpsertContext, }) { - const { t } = useTranslation(); + const {t} = useTranslation(); const [state, setState] = useState({ sortedInfo: {} @@ -51,14 +54,14 @@ export function BillsListTableComponent({ const Templates = TemplateList("bill"); const bills = billsQuery.data ? billsQuery.data.bills : []; - const { refetch } = billsQuery; + const {refetch} = billsQuery; const recordActions = (record, showView = false) => ( {showView && ( )} - + @@ -81,9 +84,9 @@ export function BillsListTableComponent({ )} @@ -126,7 +129,7 @@ export function BillsListTableComponent({ key: "is_credit_memo", sorter: (a, b) => a.is_credit_memo - b.is_credit_memo, sortOrder: state.sortedInfo.columnKey === "is_credit_memo" && state.sortedInfo.order, - render: (text, record) => + render: (text, record) => }, { title: t("bills.fields.exported"), @@ -134,7 +137,7 @@ export function BillsListTableComponent({ key: "exported", sorter: (a, b) => a.exported - b.exported, sortOrder: state.sortedInfo.columnKey === "exported" && state.sortedInfo.order, - render: (text, record) => + render: (text, record) => }, { title: t("general.labels.actions"), @@ -145,18 +148,18 @@ export function BillsListTableComponent({ ]; const handleTableChange = (pagination, filters, sorter) => { - setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); + setState({...state, filteredInfo: filters, sortedInfo: sorter}); }; const filteredBills = bills ? searchText === "" ? bills : bills.filter( - (b) => - (b.invoice_number || "").toLowerCase().includes(searchText.toLowerCase()) || - (b.vendor.name || "").toLowerCase().includes(searchText.toLowerCase()) || - (b.total || "").toString().toLowerCase().includes(searchText.toLowerCase()) - ) + (b) => + (b.invoice_number || "").toLowerCase().includes(searchText.toLowerCase()) || + (b.vendor.name || "").toLowerCase().includes(searchText.toLowerCase()) || + (b.total || "").toString().toLowerCase().includes(searchText.toLowerCase()) + ) : []; return ( @@ -165,14 +168,14 @@ export function BillsListTableComponent({ extra={ {job && job.converted ? ( <> - +
+ + + + +
); From 1469960643baefdfb9ababda36d0e493c9f2f837 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Fri, 5 Apr 2024 12:52:26 -0700 Subject: [PATCH 044/134] Resolve payment refetch. --- .../payment-modal/payment-modal.container.jsx | 16 ++++++++-------- .../payment-list-paginated.component.jsx | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/src/components/payment-modal/payment-modal.container.jsx b/client/src/components/payment-modal/payment-modal.container.jsx index f993c13b8..eb1c6ff59 100644 --- a/client/src/components/payment-modal/payment-modal.container.jsx +++ b/client/src/components/payment-modal/payment-modal.container.jsx @@ -90,7 +90,13 @@ function PaymentModalContainer({ paymentModal, toggleModalVisible, bodyshop, cur } } - if (actions.refetch) actions.refetch(updatedPayment && updatedPayment.data.update_payments.returning[0]); + if (actions.refetch) { + if (context.refetchRequiresContext) { + actions.refetch(updatedPayment && updatedPayment.data.update_payments.returning[0]); + } else { + actions.refetch(); + } + } if (enterAgain) { const prev = form.getFieldsValue(["date"]); @@ -165,13 +171,7 @@ function PaymentModalContainer({ paymentModal, toggleModalVisible, bodyshop, cur
)} -
+ diff --git a/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx b/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx index 1a5dd6774..e6f0d9822 100644 --- a/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx +++ b/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx @@ -171,7 +171,7 @@ export function PaymentsListPaginated({ } : refetch }, - context: apolloResults ? apolloResults : record + context: { ...(apolloResults ? apolloResults : record), refetchRequiresContext: true } }); }} > From a2a7c1c58cc48ef571de7d0a8caa0833bfd5d33b Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 8 Apr 2024 09:48:39 -0700 Subject: [PATCH 045/134] Add contextRefect to payment export buttons. --- .../payment-mark-export-button-component.jsx | 12 ++++++---- .../payment-reexport-button.component.jsx | 24 +++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx b/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx index 59819e03c..fc6aab627 100644 --- a/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx +++ b/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx @@ -61,11 +61,13 @@ const PaymentMarkForExportButton = ({ bodyshop, payment, refetch, setPaymentCont } }); - if (refetch) - refetch( - paymentUpdateResponse && - paymentUpdateResponse.data.update_payments.returning[0] - ); + if (refetch) { + if (context.refetchRequiresContext) { + refetch(paymentUpdateResponse && paymentUpdateResponse.data.update_payments.returning[0]); + } else { + refetch(); + } + } } else { notification["error"]({ message: t("payments.errors.exporting", { diff --git a/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx b/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx index 5325231e4..39621a2a1 100644 --- a/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx +++ b/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx @@ -5,12 +5,18 @@ import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { UPDATE_PAYMENT } from "../../graphql/payments.queries"; import { setModalContext } from "../../redux/modals/modals.actions"; +import { selectPayment } from "../../redux/modals/modals.selectors"; +import { createStructuredSelector } from "reselect"; + +const mapStateToProps = createStructuredSelector({ + paymentModal: selectPayment +}); const mapDispatchToProps = (dispatch) => ({ setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })) }); -const PaymentReexportButton = ({ payment, refetch, setPaymentContext }) => { +const PaymentReexportButton = ({ paymentModal, payment, refetch, setPaymentContext }) => { const { t } = useTranslation(); const [updatePayment, { loading }] = useMutation(UPDATE_PAYMENT); @@ -34,15 +40,19 @@ const PaymentReexportButton = ({ payment, refetch, setPaymentContext }) => { refetch }, context: { + ...paymentModal.context, ...payment, exportedat: null } }); - if (refetch) - refetch( - paymentUpdateResponse && - paymentUpdateResponse.data.update_payments.returning[0] - ); + + if (refetch) { + if (context.refetchRequiresContext) { + refetch(paymentUpdateResponse && paymentUpdateResponse.data.update_payments.returning[0]); + } else { + refetch(); + } + } } else { notification["error"]({ message: t("payments.errors.exporting", { @@ -59,4 +69,4 @@ const PaymentReexportButton = ({ payment, refetch, setPaymentContext }) => { ); }; -export default connect(null, mapDispatchToProps)(PaymentReexportButton); +export default connect(mapStateToProps, mapDispatchToProps)(PaymentReexportButton); From c3b395c99e2202fe9ce4598a5f5a04189e1fc688 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Mon, 8 Apr 2024 09:53:20 -0700 Subject: [PATCH 046/134] IO-2749 Pass Jobs data from Parts Return to Parts Order Modal Signed-off-by: Allan Carr --- .../bill-detail-edit/bill-detail-edit-return.component.jsx | 1 + .../components/bills-list-table/bills-list-table.component.jsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx b/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx index 87ea3e730..1121d9779 100644 --- a/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx +++ b/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx @@ -45,6 +45,7 @@ export function BillDetailEditReturn({ actions: {}, context: { jobId: data.bills_by_pk.jobid, + job: data.bills_by_pk.job, vendorId: data.bills_by_pk.vendorid, returnFromBill: data.bills_by_pk.id, invoiceNumber: data.bills_by_pk.invoice_number, diff --git a/client/src/components/bills-list-table/bills-list-table.component.jsx b/client/src/components/bills-list-table/bills-list-table.component.jsx index 5f5bd7011..621fe8e8e 100644 --- a/client/src/components/bills-list-table/bills-list-table.component.jsx +++ b/client/src/components/bills-list-table/bills-list-table.component.jsx @@ -60,7 +60,7 @@ export function BillsListTableComponent({ )} Date: Mon, 8 Apr 2024 13:26:37 -0400 Subject: [PATCH 047/134] - Tasks Audit Trail Additions Signed-off-by: Dave Richer --- .../task-list/task-list.container.jsx | 50 +++++++++++++--- .../task-upsert-modal.container.jsx | 60 ++++++++++++------- client/src/graphql/tasks.queries.js | 4 ++ client/src/translations/en_us/common.json | 8 ++- client/src/translations/es/common.json | 8 ++- client/src/translations/fr/common.json | 8 ++- client/src/utils/AuditTrailMappings.js | 29 ++++++++- 7 files changed, 134 insertions(+), 33 deletions(-) diff --git a/client/src/components/task-list/task-list.container.jsx b/client/src/components/task-list/task-list.container.jsx index 501bad527..709da4a9b 100644 --- a/client/src/components/task-list/task-list.container.jsx +++ b/client/src/components/task-list/task-list.container.jsx @@ -11,6 +11,9 @@ import React, {useEffect} from "react"; import TaskListComponent from "./task-list.component.jsx"; import {notification} from "antd"; import {useTranslation} from "react-i18next"; +import {useDispatch} from "react-redux"; +import {insertAuditTrail} from "../../redux/application/application.actions.js"; +import AuditTrailMapping from "../../utils/AuditTrailMappings.js"; export default function TaskListContainer({ bodyshop, @@ -25,6 +28,7 @@ export default function TaskListContainer({ const {t} = useTranslation(); const searchParams = queryString.parse(useLocation().search); const {page, sortcolumn, sortorder, deleted, completed, mine} = searchParams; + const dispatch = useDispatch(); const {loading, error, data, refetch} = useQuery( query, { @@ -80,16 +84,30 @@ export default function TaskListContainer({ const toggleCompletedStatus = async (id, currentStatus) => { const completed_at = !currentStatus ? new Date().toISOString() : null; try { - await toggleTaskCompleted({ + const toggledTask = await toggleTaskCompleted({ variables: { id: id, completed: !currentStatus, completed_at: completed_at } }); - // refetch().catch((e) => { - // console.error(`Something went wrong fetching tasks: ${e.message || ''}`); - // }); + + if (!toggledTask.errors) { + dispatch( + insertAuditTrail({ + jobid: toggledTask.data.update_tasks_by_pk.jobid, + operation: toggledTask?.data?.update_tasks_by_pk?.completed ? AuditTrailMapping.tasksCompleted( + toggledTask.data.update_tasks_by_pk.title, + currentUser.email + ) : AuditTrailMapping.tasksUncompleted( + toggledTask.data.update_tasks_by_pk.title, + currentUser.email + ), + type: toggledTask?.data?.update_tasks_by_pk?.completed ? "tasksCompleted" : "tasksUncompleted" + }) + ) + } + window.dispatchEvent(new CustomEvent('taskUpdated', { detail: {message: 'A task has been completed.'}, })); @@ -114,26 +132,42 @@ export default function TaskListContainer({ * @param currentStatus * @returns {Promise} */ + const toggleDeletedStatus = async (id, currentStatus) => { const deleted_at = !currentStatus ? new Date().toISOString() : null; try { - await toggleTaskDeleted({ + const toggledTask = await toggleTaskDeleted({ variables: { id: id, deleted: !currentStatus, deleted_at: deleted_at } }); + if (!toggledTask.errors) { + dispatch( + insertAuditTrail({ + jobid: toggledTask.data.update_tasks_by_pk.jobid, + operation: toggledTask?.data?.update_tasks_by_pk?.deleted ? AuditTrailMapping.tasksDeleted( + toggledTask.data.update_tasks_by_pk.title, + currentUser.email + ) : AuditTrailMapping.tasksUndeleted( + toggledTask.data.update_tasks_by_pk.title, + currentUser.email + ), + type: toggledTask?.data?.update_tasks_by_pk?.deleted ? "tasksDeleted" : "tasksUndeleted" + }) + ) + } + + window.dispatchEvent(new CustomEvent('taskUpdated', { detail: {message: 'A task has been deleted.'}, })); - // refetch().catch((e) => { - // console.error(`Something went wrong fetching tasks: ${e.message || ''}`); - // }); notification["success"]({ message: t("tasks.successes.deleted"), }); } catch (err) { + console.dir(err); notification["error"]({ message: t("tasks.failures.deleted"), }); diff --git a/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx index e792b0c0c..91fae5bef 100644 --- a/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx +++ b/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx @@ -18,6 +18,8 @@ import {replaceUndefinedWithNull} from "../../utils/undefinedtonull.js"; import {useNavigate} from "react-router-dom"; import axios from "axios"; import dayjs from '../../utils/day'; +import {insertAuditTrail} from "../../redux/application/application.actions.js"; +import AuditTrailMapping from "../../utils/AuditTrailMappings.js"; const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser, @@ -26,6 +28,8 @@ const mapStateToProps = createStructuredSelector({ }); const mapDispatchToProps = (dispatch) => ({ toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")), + insertAuditTrail: ({ jobid, billid, operation, type }) => + dispatch(insertAuditTrail({ jobid, billid, operation, type })) }); export function TaskUpsertModalContainer({ @@ -33,6 +37,7 @@ export function TaskUpsertModalContainer({ currentUser, taskUpsert, toggleModalVisible, + insertAuditTrail }) { const {t} = useTranslation(); const history = useNavigate(); @@ -123,14 +128,28 @@ export function TaskUpsertModalContainer({ const handleExistingTask = async (values) => { const isAssignedToDirty = values.assigned_to !== existingTask.assigned_to; - await updateTask({ + const taskData = await updateTask({ variables: { taskId: existingTask.id, task: replaceUndefinedWithNull(values) }, }); + + if (!taskData.errors) { + const oldTask = taskData?.data?.update_tasks?.returning[0]; + insertAuditTrail({ + jobid: oldTask.jobid, + operation: AuditTrailMapping.tasksUpdated( + oldTask.title, + currentUser.email + ), + type: "tasksUpdated" + }); + } + if (isAssignedToDirty) { + // TODO This is being moved serverside axios.post("/sendemail", { from: { name: bodyshop.shopname, @@ -163,7 +182,7 @@ export function TaskUpsertModalContainer({ }; const handleNewTask = async (values) => { - const newTaskID = (await insertTask({ + const newTaskData = await insertTask({ variables: { taskInput: [ { @@ -173,30 +192,29 @@ export function TaskUpsertModalContainer({ }, ], }, - // TODO: Consult Patrick, because this fails on relationship data, and an event emitter is just much easier to use - // update(cache) { - // cache.modify({ - // fields: { - // tasks(existingTasks) { - // return [{ - // ...values, - // jobid: selectedJobId || values.jobid, - // created_by: currentUser.email, - // bodyshopid: bodyshop.id - // }, ...existingTasks] - // }, - // }, - // }); - // }, - })).data.insert_tasks.returning[0].id; - + }); + + const newTask = newTaskData?.data?.insert_tasks?.returning[0]; + const newTaskID = newTask?.id; + + if (!newTaskData.errors) { + insertAuditTrail({ + jobid: newTask.jobid, + operation: AuditTrailMapping.tasksCreated( + newTask.title, + currentUser.email + ), + type: "tasksCreated" + }); + } + if (refetch) await refetch(); form.resetFields(); - toggleModalVisible(); - + // send notification to the assigned user + // TODO: This is being moved serverside axios.post("/sendemail", { from: { name: bodyshop.shopname, diff --git a/client/src/graphql/tasks.queries.js b/client/src/graphql/tasks.queries.js index 78f27a449..cc6ef9112 100644 --- a/client/src/graphql/tasks.queries.js +++ b/client/src/graphql/tasks.queries.js @@ -330,8 +330,10 @@ export const MUTATION_TOGGLE_TASK_COMPLETED = gql` mutation MUTATION_TOGGLE_TASK_COMPLETED($id: uuid!, $completed: Boolean!, $completed_at: timestamptz) { update_tasks_by_pk(pk_columns: {id: $id}, _set: {completed: $completed, completed_at: $completed_at}) { id + title completed completed_at + jobid } } `; @@ -344,8 +346,10 @@ export const MUTATION_TOGGLE_TASK_DELETED = gql` mutation MUTATION_TOGGLE_TASK_DELETED($id: uuid!, $deleted: Boolean!, $deleted_at: timestamptz) { update_tasks_by_pk(pk_columns: {id: $id}, _set: {deleted: $deleted, deleted_at: $deleted_at}) { id + title deleted deleted_at + jobid } } `; diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index d4daeaedc..e7e2b85a4 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -134,7 +134,13 @@ "jobstatuschange": "Job status changed to {{status}}.", "jobsupplement": "Job supplement imported.", "jobsuspend": "Suspend Toggle set to {{status}}", - "jobvoid": "Job has been voided." + "jobvoid": "Job has been voided.", + "tasks_created": "Task '{{title}}' created by {{createdBy}}", + "tasks_updated": "Task '{{title}}' updated by {{updatedBy}}", + "tasks_deleted": "Task '{{title}}' deleted by {{deletedBy}}", + "tasks_undeleted": "Task '{{title}}' undeleted by {{undeletedBy}}", + "tasks_completed": "Task '{{title}}' completed by {{completedBy}}", + "tasks_uncompleted": "Task '{{title}}' uncompleted by {{uncompletedBy}}" } }, "billlines": { diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 26e5c69e7..46a2b6cd7 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -134,7 +134,13 @@ "jobstatuschange": "", "jobsupplement": "", "jobsuspend": "", - "jobvoid": "" + "jobvoid": "", + "tasks_created": "", + "tasks_updated": "", + "tasks_deleted": "", + "tasks_undeleted": "", + "tasks_completed": "", + "tasks_uncompleted": "" } }, "billlines": { diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 21bedcf52..e2703dd1b 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -134,7 +134,13 @@ "jobstatuschange": "", "jobsupplement": "", "jobsuspend": "", - "jobvoid": "" + "jobvoid": "", + "tasks_created": "", + "tasks_updated": "", + "tasks_deleted": "", + "tasks_undeleted": "", + "tasks_completed": "", + "tasks_uncompleted": "" } }, "billlines": { diff --git a/client/src/utils/AuditTrailMappings.js b/client/src/utils/AuditTrailMappings.js index f2de52319..b0f8e0924 100644 --- a/client/src/utils/AuditTrailMappings.js +++ b/client/src/utils/AuditTrailMappings.js @@ -39,7 +39,34 @@ const AuditTrailMapping = { jobstatuschange: (status) => i18n.t("audit_trail.messages.jobstatuschange", { status }), jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"), jobsuspend: (status) => i18n.t("audit_trail.messages.jobsuspend", { status }), - jobvoid: () => i18n.t("audit_trail.messages.jobvoid") + jobvoid: () => i18n.t("audit_trail.messages.jobvoid"), +// Tasks Entries + tasksCreated: (title, createdBy) => i18n.t("audit_trail.messages.tasks_created", { + title, + createdBy + }), + tasksUpdated: (title, updatedBy) => i18n.t("audit_trail.messages.tasks_updated", { + title, + updatedBy + }), + tasksDeleted: (title, deletedBy) => i18n.t("audit_trail.messages.tasks_deleted", { + title, + deletedBy + }), + tasksUndeleted: (title, undeletedBy) => i18n.t("audit_trail.messages.tasks_undeleted", { + title, + undeletedBy + }), + tasksCompleted: (title, completedBy) => i18n.t("audit_trail.messages.tasks_completed", { + title, + completedBy + }), + tasksUncompleted: (title, uncompletedBy) => i18n.t("audit_trail.messages.tasks_uncompleted", { + title, + uncompletedBy + }), + + }; export default AuditTrailMapping; From b8e4520366cc3c8e1de9e9a8ec33f2444928fe59 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 8 Apr 2024 11:05:12 -0700 Subject: [PATCH 048/134] Resolve ES Lint error. --- .../payment-mark-export-button-component.jsx | 9 ++++++--- .../payment-reexport-button.component.jsx | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx b/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx index fc6aab627..8511b7506 100644 --- a/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx +++ b/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx @@ -8,16 +8,18 @@ import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { UPDATE_PAYMENT } from "../../graphql/payments.queries"; import { setModalContext } from "../../redux/modals/modals.actions"; import { selectCurrentUser } from "../../redux/user/user.selectors"; +import { selectPayment } from "../../redux/modals/modals.selectors"; const mapStateToProps = createStructuredSelector({ - currentUser: selectCurrentUser + currentUser: selectCurrentUser, + paymentModal: selectPayment }); const mapDispatchToProps = (dispatch) => ({ setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })) }); -const PaymentMarkForExportButton = ({ bodyshop, payment, refetch, setPaymentContext, currentUser }) => { +const PaymentMarkForExportButton = ({ bodyshop, payment, refetch, setPaymentContext, currentUser, paymentModal }) => { const { t } = useTranslation(); const [insertExportLog, { loading: exportLogLoading }] = useMutation(INSERT_EXPORT_LOG); const [updatePayment, { loading: updatePaymentLoading }] = useMutation(UPDATE_PAYMENT); @@ -56,13 +58,14 @@ const PaymentMarkForExportButton = ({ bodyshop, payment, refetch, setPaymentCont refetch }, context: { + ...paymentModal.context, ...payment, exportedat: today } }); if (refetch) { - if (context.refetchRequiresContext) { + if (paymentModal.context.refetchRequiresContext) { refetch(paymentUpdateResponse && paymentUpdateResponse.data.update_payments.returning[0]); } else { refetch(); diff --git a/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx b/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx index 39621a2a1..434377e86 100644 --- a/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx +++ b/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx @@ -47,7 +47,7 @@ const PaymentReexportButton = ({ paymentModal, payment, refetch, setPaymentConte }); if (refetch) { - if (context.refetchRequiresContext) { + if (paymentModal.context.refetchRequiresContext) { refetch(paymentUpdateResponse && paymentUpdateResponse.data.update_payments.returning[0]); } else { refetch(); From 88a71dd647c7d1415f66511d8511e4859111a4a0 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Mon, 8 Apr 2024 12:23:04 -0700 Subject: [PATCH 049/134] IO-2731 Payment Edit Signed-off-by: Allan Carr --- .../payment-mark-export-button-component.jsx | 19 ++++++++--- .../payment-modal/payment-modal.container.jsx | 17 ++++++---- .../payment-reexport-button.component.jsx | 33 +++++++++++++++---- .../payment-list-paginated.component.jsx | 7 ++-- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 7 files changed, 59 insertions(+), 20 deletions(-) diff --git a/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx b/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx index 5f8cb395a..5ffdc58ee 100644 --- a/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx +++ b/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx @@ -7,10 +7,12 @@ import { createStructuredSelector } from "reselect"; import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { UPDATE_PAYMENT } from "../../graphql/payments.queries"; import { setModalContext } from "../../redux/modals/modals.actions"; +import { selectPayment } from "../../redux/modals/modals.selectors"; import { selectCurrentUser } from "../../redux/user/user.selectors"; const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser, + paymentModal: selectPayment, }); const mapDispatchToProps = (dispatch) => ({ @@ -24,6 +26,7 @@ const PaymentMarkForExportButton = ({ refetch, setPaymentContext, currentUser, + paymentModal, }) => { const { t } = useTranslation(); const [insertExportLog, { loading: exportLogLoading }] = @@ -65,16 +68,22 @@ const PaymentMarkForExportButton = ({ refetch, }, context: { + ...paymentModal.context, ...payment, exportedat: today, }, }); - if (refetch) - refetch( - paymentUpdateResponse && - paymentUpdateResponse.data.update_payments.returning[0] - ); + if (refetch) { + if (paymentModal.context.refetchRequiresContext) { + refetch( + paymentUpdateResponse && + paymentUpdateResponse.data.update_payments.returning[0] + ); + } else { + refetch(); + } + } } else { notification["error"]({ message: t("payments.errors.exporting", { diff --git a/client/src/components/payment-modal/payment-modal.container.jsx b/client/src/components/payment-modal/payment-modal.container.jsx index e53894920..11666914d 100644 --- a/client/src/components/payment-modal/payment-modal.container.jsx +++ b/client/src/components/payment-modal/payment-modal.container.jsx @@ -97,16 +97,21 @@ function PaymentModalContainer({ }); if (!!!updatedPayment.errors) { - notification["success"]({ message: t("payments.successes.payment") }); + notification["success"]({ message: t("payments.successes.paymentupdate") }); } else { - notification["error"]({ message: t("payments.errors.payment") }); + notification["error"]({ message: t("payments.errors.paymentupdate") }); } } - if (actions.refetch) - actions.refetch( - updatedPayment && updatedPayment.data.update_payments.returning[0] - ); + if (actions.refetch) { + if (context.refetchRequiresContext) { + actions.refetch( + updatedPayment && updatedPayment.data.update_payments.returning[0] + ); + } else { + actions.refetch(); + } + } if (enterAgain) { const prev = form.getFieldsValue(["date"]); diff --git a/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx b/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx index a7434e76d..fa6063d67 100644 --- a/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx +++ b/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx @@ -3,15 +3,25 @@ import { Button, notification } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; import { UPDATE_PAYMENT } from "../../graphql/payments.queries"; import { setModalContext } from "../../redux/modals/modals.actions"; +import { selectPayment } from "../../redux/modals/modals.selectors"; +const mapStateToProps = createStructuredSelector({ + paymentModal: selectPayment, +}); const mapDispatchToProps = (dispatch) => ({ setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })), }); -const PaymentReexportButton = ({ payment, refetch, setPaymentContext }) => { +const PaymentReexportButton = ({ + paymentModal, + payment, + refetch, + setPaymentContext, +}) => { const { t } = useTranslation(); const [updatePayment, { loading }] = useMutation(UPDATE_PAYMENT); @@ -35,15 +45,21 @@ const PaymentReexportButton = ({ payment, refetch, setPaymentContext }) => { refetch, }, context: { + ...paymentModal.context, ...payment, exportedat: null, }, }); - if (refetch) - refetch( - paymentUpdateResponse && - paymentUpdateResponse.data.update_payments.returning[0] - ); + if (refetch) { + if (paymentModal.context.refetchRequiresContext) { + refetch( + paymentUpdateResponse && + paymentUpdateResponse.data.update_payments.returning[0] + ); + } else { + refetch(); + } + } } else { notification["error"]({ message: t("payments.errors.exporting", { @@ -64,4 +80,7 @@ const PaymentReexportButton = ({ payment, refetch, setPaymentContext }) => { ); }; -export default connect(null, mapDispatchToProps)(PaymentReexportButton); +export default connect( + mapStateToProps, + mapDispatchToProps +)(PaymentReexportButton); diff --git a/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx b/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx index 86ad50cb8..6fdfd108a 100644 --- a/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx +++ b/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx @@ -14,11 +14,11 @@ import { selectBodyshop } from "../../redux/user/user.selectors"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter"; import { TemplateList } from "../../utils/TemplateConstants"; +import { pageLimit } from "../../utils/config"; import { alphaSort } from "../../utils/sorters"; import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import PrintWrapperComponent from "../print-wrapper/print-wrapper.component"; -import {pageLimit} from "../../utils/config"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser @@ -184,7 +184,10 @@ export function PaymentsListPaginated({ } : refetch, }, - context: apolloResults ? apolloResults : record, + context: { + ...(apolloResults ? apolloResults : record), + refetchRequiresContext: true, + }, }); }} > diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 210f04c0f..d54e65c1a 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -2332,6 +2332,7 @@ "markexported": "Payment(s) marked exported.", "markreexported": "Payment marked for re-export successfully", "payment": "Payment created successfully. ", + "paymentupdate": "Payment updated successfully. ", "stripe": "Credit card transaction charged successfully." } }, diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index d1a760146..30ab017ba 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2331,6 +2331,7 @@ "markexported": "", "markreexported": "", "payment": "", + "paymentupdate": "", "stripe": "" } }, diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 87253cf17..8a77e2231 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -2331,6 +2331,7 @@ "markexported": "", "markreexported": "", "payment": "", + "paymentupdate": "", "stripe": "" } }, From e137feca20c187091643fe86848541d977ce7b6a Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 8 Apr 2024 14:20:34 -0700 Subject: [PATCH 050/134] Resolve ESLint Warnings --- .../job-search-select.component.jsx | 20 +++++++++++-------- .../payment-mark-export-button-component.jsx | 1 - .../payment-modal/payment-modal.container.jsx | 1 - .../payment-reexport-button.component.jsx | 4 ---- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/client/src/components/job-search-select/job-search-select.component.jsx b/client/src/components/job-search-select/job-search-select.component.jsx index 117d0f407..90ced9d1c 100644 --- a/client/src/components/job-search-select/job-search-select.component.jsx +++ b/client/src/components/job-search-select/job-search-select.component.jsx @@ -1,3 +1,4 @@ +import { LoadingOutlined } from "@ant-design/icons"; import { useLazyQuery } from "@apollo/client"; import { Select, Space, Spin, Tag } from "antd"; import _ from "lodash"; @@ -6,8 +7,6 @@ import { useTranslation } from "react-i18next"; import { SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE, SEARCH_JOBS_FOR_AUTOCOMPLETE } from "../../graphql/jobs.queries"; import AlertComponent from "../alert/alert.component"; import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component"; -import { SearchOutlined } from "@ant-design/icons"; -import { LoadingOutlined } from "@ant-design/icons"; const { Option } = Select; @@ -19,9 +18,14 @@ const JobSearchSelect = ( const [theOptions, setTheOptions] = useState([]); const [callSearch, { loading, error, data }] = useLazyQuery(SEARCH_JOBS_FOR_AUTOCOMPLETE, {}); - const [callIdSearch, { loading: idLoading, error: idError, data: idData }] = useLazyQuery( - SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE - ); + const [ + callIdSearch, + { + //loading: idLoading, + error: idError, + data: idData + } + ] = useLazyQuery(SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE); const executeSearch = (v) => { if (v && v.variables?.search !== "" && v.variables.search.length >= 2) callSearch(v); @@ -86,9 +90,9 @@ const JobSearchSelect = ( {`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${ o.ro_number || t("general.labels.na") - } | ${OwnerNameDisplayFunction(o)} | ${ - o.v_model_yr || "" - } ${o.v_make_desc || ""} ${o.v_model_desc || ""}`} + } | ${OwnerNameDisplayFunction(o)} | ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${ + o.v_model_desc || "" + }`} {o.status} diff --git a/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx b/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx index cfd1c1f58..f82832ddc 100644 --- a/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx +++ b/client/src/components/payment-mark-export-button/payment-mark-export-button-component.jsx @@ -9,7 +9,6 @@ import { UPDATE_PAYMENT } from "../../graphql/payments.queries"; import { setModalContext } from "../../redux/modals/modals.actions"; import { selectPayment } from "../../redux/modals/modals.selectors"; import { selectCurrentUser } from "../../redux/user/user.selectors"; -import { selectPayment } from "../../redux/modals/modals.selectors"; const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser, diff --git a/client/src/components/payment-modal/payment-modal.container.jsx b/client/src/components/payment-modal/payment-modal.container.jsx index 8c5ca9e24..6535005d2 100644 --- a/client/src/components/payment-modal/payment-modal.container.jsx +++ b/client/src/components/payment-modal/payment-modal.container.jsx @@ -32,7 +32,6 @@ function PaymentModalContainer({paymentModal, toggleModalVisible, bodyshop }) { const { t } = useTranslation(); const { context, actions, open } = paymentModal; - const smartRefetch = context?.smartRefetch || false; const [loading, setLoading] = useState(false); const handleFinish = async (values) => { diff --git a/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx b/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx index b7ba4f4ac..3abd5bfd5 100644 --- a/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx +++ b/client/src/components/payment-reexport-button/payment-reexport-button.component.jsx @@ -7,15 +7,11 @@ import { createStructuredSelector } from "reselect"; import { UPDATE_PAYMENT } from "../../graphql/payments.queries"; import { setModalContext } from "../../redux/modals/modals.actions"; import { selectPayment } from "../../redux/modals/modals.selectors"; -import { createStructuredSelector } from "reselect"; const mapStateToProps = createStructuredSelector({ paymentModal: selectPayment }); -const mapStateToProps = createStructuredSelector({ - paymentModal: selectPayment, -}); const mapDispatchToProps = (dispatch) => ({ setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })) }); From 706984a53b5677a277eceede440560a61a7baafc Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 8 Apr 2024 14:27:33 -0700 Subject: [PATCH 051/134] Resolve CCC supplement import. --- .../jobs-available-table/jobs-available-table.container.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/jobs-available-table/jobs-available-table.container.jsx b/client/src/components/jobs-available-table/jobs-available-table.container.jsx index 26776a8c1..2c851d3f1 100644 --- a/client/src/components/jobs-available-table/jobs-available-table.container.jsx +++ b/client/src/components/jobs-available-table/jobs-available-table.container.jsx @@ -237,7 +237,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail executeFunction: true, rome: ResolveCCCLineIssues, promanager: ResolveCCCLineIssues, - args: [(supp, bodyshop)] + args: [supp, bodyshop] }); await InstanceRenderManager({ From f5b8bf1d74219b87c65d0bc8cb21639519bd6f9c Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 8 Apr 2024 14:32:02 -0700 Subject: [PATCH 052/134] Resolve unnecessary import. --- .../payments-list-paginated/payment-list-paginated.component.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx b/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx index 67a716ff2..c325c0f7a 100644 --- a/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx +++ b/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx @@ -19,7 +19,6 @@ import { alphaSort } from "../../utils/sorters"; import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import PrintWrapperComponent from "../print-wrapper/print-wrapper.component"; -import { pageLimit } from "../../utils/config"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser From df0f8ef9dcb0421a5c48ed1ee792d76b0bc7674d Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Mon, 8 Apr 2024 22:07:14 -0400 Subject: [PATCH 053/134] Progress Signed-off-by: Dave Richer --- .../job-search-select.component.jsx | 2 +- .../job-totals.table.totals.component.jsx | 3 ++ .../task-list/task-list.component.jsx | 30 +++++++++++-------- .../task-list/task-list.container.jsx | 4 ++- .../jobs-detail.page.component.jsx | 2 +- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/client/src/components/job-search-select/job-search-select.component.jsx b/client/src/components/job-search-select/job-search-select.component.jsx index 4282014ff..0d175b91a 100644 --- a/client/src/components/job-search-select/job-search-select.component.jsx +++ b/client/src/components/job-search-select/job-search-select.component.jsx @@ -31,7 +31,7 @@ const JobSearchSelect = ( const [ callIdSearch, { - //loading: idLoading, + loading: idLoading, error: idError, data: idData } diff --git a/client/src/components/job-totals-table/job-totals.table.totals.component.jsx b/client/src/components/job-totals-table/job-totals.table.totals.component.jsx index 5be1fb010..f09483975 100644 --- a/client/src/components/job-totals-table/job-totals.table.totals.component.jsx +++ b/client/src/components/job-totals-table/job-totals.table.totals.component.jsx @@ -21,6 +21,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) { const { t } = useTranslation(); const data = useMemo(() => { + return [ { key: t("jobs.labels.subtotal"), @@ -165,6 +166,8 @@ export function JobTotalsTableTotals({ bodyshop, job }) { bold: true } ]; + // TODO: was removed by Patrick during a CI bug fix. + // eslint-disable-next-line react-hooks/exhaustive-deps }, [job.job_totals, job.cieca_pft, t, bodyshop.md_responsibility_centers]); const columns = [ diff --git a/client/src/components/task-list/task-list.component.jsx b/client/src/components/task-list/task-list.component.jsx index 75100e0e3..17618ff20 100644 --- a/client/src/components/task-list/task-list.component.jsx +++ b/client/src/components/task-list/task-list.component.jsx @@ -90,7 +90,8 @@ function TaskListComponent({ relationshipType, relationshipId, onlyMine, - parentJobId + parentJobId, + showRo = true, }) { const {t} = useTranslation(); @@ -126,18 +127,23 @@ function TaskListComponent({ ); } + if (showRo) { + columns.push( + { + title: t("tasks.fields.job.ro_number"), + dataIndex: ["job", "ro_number"], + key: "job.ro_number", + width: '8%', + render: (text, record) => + record.job + ? {record.job.ro_number || t("general.labels.na")} + : t("general.labels.na") + } + ); + } + columns.push( - { - title: t("tasks.fields.job.ro_number"), - dataIndex: ["job", "ro_number"], - key: "job.ro_number", - width: '8%', - render: (text, record) => - record.job - ? {record.job.ro_number || t("general.labels.na")} - : t("general.labels.na") - }, { title: t("tasks.fields.jobline"), dataIndex: ["jobline", "id"], diff --git a/client/src/components/task-list/task-list.container.jsx b/client/src/components/task-list/task-list.container.jsx index 709da4a9b..f0f76cac7 100644 --- a/client/src/components/task-list/task-list.container.jsx +++ b/client/src/components/task-list/task-list.container.jsx @@ -23,7 +23,8 @@ export default function TaskListContainer({ relationshipId, currentUser, onlyMine, - parentJobId + parentJobId, + showRo = true }) { const {t} = useTranslation(); const searchParams = queryString.parse(useLocation().search); @@ -190,6 +191,7 @@ export default function TaskListContainer({ relationshipType={relationshipType} relationshipId={relationshipId} onlyMine={onlyMine} + showRo={showRo} parentJobId={parentJobId} /> ); diff --git a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx index a600faf57..fb7ab9d47 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx @@ -412,7 +412,7 @@ export function JobsDetailPage({ children: + titleTranslation='tasks.titles.job_tasks' showRo={false}/> }, ]} /> From 33c282051bf892c15fb87d3c487df569770e9acc Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Mon, 8 Apr 2024 22:25:07 -0400 Subject: [PATCH 054/134] Fix Formatting issues Signed-off-by: Dave Richer --- client/src/App/App.jsx | 30 +- .../bills-list-table.component.jsx | 126 +++--- .../components/header/header.component.jsx | 389 +++++++++--------- .../job-lines-expander.component.jsx | 142 +++---- .../job-detail-lines/job-lines.component.jsx | 29 +- .../job-search-select.component.jsx | 69 ++-- .../job-totals.table.totals.component.jsx | 3 +- .../jobs-detail-header-actions.component.jsx | 339 +++++++-------- .../jobs-list-paginated.component.jsx | 60 +-- .../jobs-list/jobs-list.component.jsx | 91 ++-- .../parts-order-list-table.component.jsx | 196 ++++----- .../task-list/task-list.component.jsx | 325 ++++++++------- .../task-list/task-list.container.jsx | 146 +++---- .../task-upsert-modal.component.jsx | 176 ++++---- .../task-upsert-modal.container.jsx | 229 +++++------ client/src/graphql/bills.queries.js | 2 +- client/src/graphql/jobs.queries.js | 36 +- client/src/graphql/tasks.queries.js | 126 +++--- .../jobs-detail.page.component.jsx | 264 ++++++------ .../pages/manage/manage.page.component.jsx | 302 +++++++------- .../src/pages/tasks/allTasksPageContainer.jsx | 65 ++- .../src/pages/tasks/myTasksPageContainer.jsx | 41 +- client/src/pages/tasks/taskPageTypes.jsx | 4 +- .../src/pages/tasks/tasks.page.component.jsx | 33 +- client/src/redux/modals/modals.reducer.js | 2 +- client/src/translations/en_us/common.json | 88 ++-- client/src/translations/es/common.json | 86 ++-- client/src/translations/fr/common.json | 86 ++-- client/src/utils/AuditTrailMappings.js | 58 +-- client/src/utils/undefinedtonull.js | 2 +- package-lock.json | 16 + package.json | 1 + server/email/generateTemplate.js | 29 +- server/email/sendemail.js | 34 +- 34 files changed, 1805 insertions(+), 1820 deletions(-) diff --git a/client/src/App/App.jsx b/client/src/App/App.jsx index 56a3e5f19..50bde585a 100644 --- a/client/src/App/App.jsx +++ b/client/src/App/App.jsx @@ -147,23 +147,19 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline /> } > - { - import.meta.env.PRODUCT_FRUITS_DISABLED !== 'true' && ( - - ) - } + ({ - setBillEnterContext: (context) => dispatch(setModalContext({ - context: context, - modal: "billEnter" - })), - setReconciliationContext: (context) => dispatch(setModalContext({ - context: context, - modal: "reconciliation" - })), - setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})), + setBillEnterContext: (context) => + dispatch( + setModalContext({ + context: context, + modal: "billEnter" + }) + ), + setReconciliationContext: (context) => + dispatch( + setModalContext({ + context: context, + modal: "reconciliation" + }) + ), + setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" })) }); export function BillsListTableComponent({ - bodyshop, - jobRO, - job, - billsQuery, - handleOnRowClick, - setBillEnterContext, - setReconciliationContext, - setTaskUpsertContext, - }) { - const {t} = useTranslation(); + bodyshop, + jobRO, + job, + billsQuery, + handleOnRowClick, + setBillEnterContext, + setReconciliationContext, + setTaskUpsertContext +}) { + const { t } = useTranslation(); const [state, setState] = useState({ sortedInfo: {} @@ -54,27 +60,29 @@ export function BillsListTableComponent({ const Templates = TemplateList("bill"); const bills = billsQuery.data ? billsQuery.data.bills : []; - const {refetch} = billsQuery; - + const { refetch } = billsQuery; const recordActions = (record, showView = false) => ( {showView && ( )} - - + )} @@ -129,7 +137,7 @@ export function BillsListTableComponent({ key: "is_credit_memo", sorter: (a, b) => a.is_credit_memo - b.is_credit_memo, sortOrder: state.sortedInfo.columnKey === "is_credit_memo" && state.sortedInfo.order, - render: (text, record) => + render: (text, record) => }, { title: t("bills.fields.exported"), @@ -137,7 +145,7 @@ export function BillsListTableComponent({ key: "exported", sorter: (a, b) => a.exported - b.exported, sortOrder: state.sortedInfo.columnKey === "exported" && state.sortedInfo.order, - render: (text, record) => + render: (text, record) => }, { title: t("general.labels.actions"), @@ -148,18 +156,18 @@ export function BillsListTableComponent({ ]; const handleTableChange = (pagination, filters, sorter) => { - setState({...state, filteredInfo: filters, sortedInfo: sorter}); + setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); }; const filteredBills = bills ? searchText === "" ? bills : bills.filter( - (b) => - (b.invoice_number || "").toLowerCase().includes(searchText.toLowerCase()) || - (b.vendor.name || "").toLowerCase().includes(searchText.toLowerCase()) || - (b.total || "").toString().toLowerCase().includes(searchText.toLowerCase()) - ) + (b) => + (b.invoice_number || "").toLowerCase().includes(searchText.toLowerCase()) || + (b.vendor.name || "").toLowerCase().includes(searchText.toLowerCase()) || + (b.total || "").toString().toLowerCase().includes(searchText.toLowerCase()) + ) : []; return ( @@ -168,14 +176,14 @@ export function BillsListTableComponent({ extra={ {job && job.converted ? ( <> - - - + + - - + + @@ -283,10 +277,10 @@ export function JobsDetailPage({ return (
- - - - + + + +
- - - - + + + + history({search: `?tab=${key}`})} - tabBarStyle={{fontWeight: "bold", borderBottom: "10px"}} + onChange={(key) => history({ search: `?tab=${key}` })} + tabBarStyle={{ fontWeight: "bold", borderBottom: "10px" }} items={[ { key: "general", - icon: , + icon: , label: t("menus.jobsdetail.general"), forceRender: true, - children: + children: }, { key: "repairdata", - icon: , + icon: , label: t("menus.jobsdetail.repairdata"), forceRender: true, - children: + children: }, { key: "rates", - icon: , + icon: , label: t("menus.jobsdetail.rates"), forceRender: true, - children: + children: }, { key: "totals", - icon: , + icon: , label: t("menus.jobsdetail.totals"), - children: + children: }, { key: "partssublet", - icon: , - label: HasFeatureAccess({featureName: "bills", bodyshop}) + icon: , + label: HasFeatureAccess({ featureName: "bills", bodyshop }) ? t("menus.jobsdetail.partssublet") : t("menus.jobsdetail.parts"), - children: + children: }, ...(InstanceRenderManager({ imex: true, rome: true, - promanager: HasFeatureAccess({featureName: "timetickets", bodyshop}) + promanager: HasFeatureAccess({ featureName: "timetickets", bodyshop }) }) ? [ - { - key: "labor", - icon: , - label: t("menus.jobsdetail.labor"), - children: - } - ] + { + key: "labor", + icon: , + label: t("menus.jobsdetail.labor"), + children: + } + ] : []), { key: "lifecycle", - icon: , + icon: , label: t("menus.jobsdetail.lifecycle"), - children: + children: }, { key: "dates", - icon: , + icon: , label: t("menus.jobsdetail.dates"), forceRender: true, - children: + children: }, ...(InstanceRenderManager({ imex: true, rome: true, - promanager: HasFeatureAccess({featureName: "media", bodyshop}) + promanager: HasFeatureAccess({ featureName: "media", bodyshop }) }) ? [ - { - key: "documents", - icon: , - label: t("jobs.labels.documents"), - children: bodyshop.uselocalmediaserver ? ( - - ) : ( - - ) - } - ] + { + key: "documents", + icon: , + label: t("jobs.labels.documents"), + children: bodyshop.uselocalmediaserver ? ( + + ) : ( + + ) + } + ] : []), { key: "notes", - icon: , + icon: , label: t("jobs.labels.notes"), - children: + children: }, { key: "audit", - icon: , + icon: , label: t("jobs.labels.audit"), - children: + children: }, { - key: 'tasks', - icon: , - label: - {t("jobs.labels.tasks")}{job.tasks_aggregate.aggregate.count > 0 && - } - , - children: - }, + key: "tasks", + icon: , + label: ( + + {t("jobs.labels.tasks")} + {job.tasks_aggregate.aggregate.count > 0 && } + + ), + children: ( + + ) + } ]} /> @@ -424,7 +426,7 @@ export function JobsDetailPage({ export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailPage); const transformJobToForm = (job) => { - const transformedJob = {...job}; + const transformedJob = { ...job }; transformedJob.parts_tax_rates = Object.keys(transformedJob.parts_tax_rates).reduce((acc, parttype) => { acc[parttype] = Object.keys(transformedJob.parts_tax_rates[parttype]).reduce((innerAcc, key) => { diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx index 476642332..d0f5ffeb4 100644 --- a/client/src/pages/manage/manage.page.component.jsx +++ b/client/src/pages/manage/manage.page.component.jsx @@ -1,10 +1,10 @@ -import {Button, Collapse, FloatButton, Layout, Space, Spin, Tag} from "antd"; +import { Button, Collapse, FloatButton, Layout, Space, Spin, Tag } from "antd"; // import preval from "preval.macro"; -import React, {lazy, Suspense, useEffect, useState} from "react"; -import {useTranslation} from "react-i18next"; -import {connect} from "react-redux"; -import {Link, Route, Routes} from "react-router-dom"; -import {createStructuredSelector} from "reselect"; +import React, { lazy, Suspense, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { Link, Route, Routes } from "react-router-dom"; +import { createStructuredSelector } from "reselect"; import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component"; import ChatAffixContainer from "../../components/chat-affix/chat-affix.container"; import ConflictComponent from "../../components/conflict/conflict.component"; @@ -17,26 +17,21 @@ import TestComponent from "../../components/_test/test.page"; import HeaderContainer from "../../components/header/header.container"; import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; import PartnerPingComponent from "../../components/partner-ping/partner-ping.component"; -import PrintCenterModalContainer - from "../../components/print-center-modal/print-center-modal.container"; +import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container"; import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component"; -import {requestForToken} from "../../firebase/firebase.utils"; -import {selectBodyshop, selectInstanceConflict} from "../../redux/user/user.selectors"; +import { requestForToken } from "../../firebase/firebase.utils"; +import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors"; import UpdateAlert from "../../components/update-alert/update-alert.component"; -import {setJoyRideFinished} from "../../redux/application/application.actions.js"; -import { - selectEnableJoyRide, - selectJoyRideSteps -} from "../../redux/application/application.selectors.js"; +import { setJoyRideFinished } from "../../redux/application/application.actions.js"; +import { selectEnableJoyRide, selectJoyRideSteps } from "../../redux/application/application.selectors.js"; import InstanceRenderManager from "../../utils/instanceRenderMgr.js"; import "./manage.page.styles.scss"; - const JobsPage = lazy(() => import("../jobs/jobs.page")); -const CardPaymentModalContainer = lazy(() => - import("../../components/card-payment-modal/card-payment-modal.container.") +const CardPaymentModalContainer = lazy( + () => import("../../components/card-payment-modal/card-payment-modal.container.") ); const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page.container")); @@ -64,8 +59,8 @@ const JobCostingModal = lazy(() => import("../../components/job-costing-modal/jo const ReportCenterModal = lazy(() => import("../../components/report-center-modal/report-center-modal.container")); const BillEnterModalContainer = lazy(() => import("../../components/bill-enter-modal/bill-enter-modal.container")); const TimeTicketModalContainer = lazy(() => import("../../components/time-ticket-modal/time-ticket-modal.container")); -const TimeTicketModalTask = lazy(() => - import("../../components/time-ticket-task-modal/time-ticket-task-modal.container") +const TimeTicketModalTask = lazy( + () => import("../../components/time-ticket-task-modal/time-ticket-task-modal.container") ); const PaymentModalContainer = lazy(() => import("../../components/payment-modal/payment-modal.container")); const ProductionListPage = lazy(() => import("../production-list/production-list.container")); @@ -106,7 +101,7 @@ const MyTasksPage = lazy(() => import("../tasks/myTasksPageContainer.jsx")); const AllTasksPage = lazy(() => import("../tasks/allTasksPageContainer.jsx")); const TaskUpsertModalContainer = lazy(() => import("../../components/task-upsert-modal/task-upsert-modal.container")); -const {Content, Footer} = Layout; +const { Content, Footer } = Layout; const mapStateToProps = createStructuredSelector({ conflict: selectInstanceConflict, @@ -119,8 +114,8 @@ const mapDispatchToProps = (dispatch) => ({ setJoyRideFinished: (steps) => dispatch(setJoyRideFinished(steps)) }); -export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyRideFinished}) { - const {t} = useTranslation(); +export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyRideFinished }) { + const { t } = useTranslation(); const [chatVisible] = useState(false); const [tours, setTours] = useState([]); @@ -156,264 +151,266 @@ export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyR /> } > - + - - - - - - - - - - + + + + + + + + + + - }/> - }/> + } /> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - - } + }> + + + } /> }> - - } + }> + + + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> @@ -426,16 +423,16 @@ export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyR }> - + }> + } /> }> - + }> + } /> @@ -443,8 +440,8 @@ export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyR }> - + }> + } /> @@ -452,56 +449,56 @@ export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyR }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> @@ -509,65 +506,65 @@ export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyR }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> - }/> + } /> }> - + }> + } /> }> - + }> + } /> }> - + }> + } /> @@ -577,16 +574,16 @@ export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyR let PageContent; - if (conflict) PageContent = ; - else if (bodyshop && bodyshop.sub_status !== "active") PageContent = ; + if (conflict) PageContent = ; + else if (bodyshop && bodyshop.sub_status !== "active") PageContent = ; else PageContent = AppRouteTable; return ( <> - - - - + + + + - - } showDialog> + + } showDialog> {PageContent} - +