@@ -147,8 +147,6 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{
|
|
||||||
import.meta.env.PRODUCT_FRUITS_DISABLED !== 'true' && (
|
|
||||||
<ProductFruits
|
<ProductFruits
|
||||||
workspaceCode={InstanceRenderMgr({
|
workspaceCode={InstanceRenderMgr({
|
||||||
imex: null,
|
imex: null,
|
||||||
@@ -162,8 +160,6 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
|
|||||||
username: currentUser.email
|
username: currentUser.email
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
|
|||||||
@@ -18,19 +18,25 @@ import {FaTasks} from "react-icons/fa";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({
|
setBillEnterContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "billEnter"
|
modal: "billEnter"
|
||||||
})),
|
})
|
||||||
setReconciliationContext: (context) => dispatch(setModalContext({
|
),
|
||||||
|
setReconciliationContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "reconciliation"
|
modal: "reconciliation"
|
||||||
})),
|
})
|
||||||
setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})),
|
),
|
||||||
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function BillsListTableComponent({
|
export function BillsListTableComponent({
|
||||||
@@ -41,7 +47,7 @@ export function BillsListTableComponent({
|
|||||||
handleOnRowClick,
|
handleOnRowClick,
|
||||||
setBillEnterContext,
|
setBillEnterContext,
|
||||||
setReconciliationContext,
|
setReconciliationContext,
|
||||||
setTaskUpsertContext,
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -56,7 +62,6 @@ export function BillsListTableComponent({
|
|||||||
const bills = billsQuery.data ? billsQuery.data.bills : [];
|
const bills = billsQuery.data ? billsQuery.data.bills : [];
|
||||||
const { refetch } = billsQuery;
|
const { refetch } = billsQuery;
|
||||||
|
|
||||||
|
|
||||||
const recordActions = (record, showView = false) => (
|
const recordActions = (record, showView = false) => (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
{showView && (
|
{showView && (
|
||||||
@@ -64,14 +69,17 @@ export function BillsListTableComponent({
|
|||||||
<EditFilled />
|
<EditFilled />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button title={t('tasks.buttons.create')} onClick={() => {
|
<Button
|
||||||
|
title={t("tasks.buttons.create")}
|
||||||
|
onClick={() => {
|
||||||
setTaskUpsertContext({
|
setTaskUpsertContext({
|
||||||
context: {
|
context: {
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
billid: record.id
|
billid: record.id
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<FaTasks />
|
<FaTasks />
|
||||||
</Button>
|
</Button>
|
||||||
<BillDeleteButton bill={record} jobid={job.id} />
|
<BillDeleteButton bill={record} jobid={job.id} />
|
||||||
|
|||||||
@@ -31,23 +31,14 @@ import {Layout, Menu, Switch, Tooltip} from "antd";
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { BsKanban } from "react-icons/bs";
|
import { BsKanban } from "react-icons/bs";
|
||||||
import {
|
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar, FaTasks } from "react-icons/fa";
|
||||||
FaCalendarAlt,
|
|
||||||
FaCarCrash,
|
|
||||||
FaCreditCard,
|
|
||||||
FaFileInvoiceDollar,
|
|
||||||
FaTasks
|
|
||||||
} from "react-icons/fa";
|
|
||||||
import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from "react-icons/gi";
|
import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from "react-icons/gi";
|
||||||
import { IoBusinessOutline } from "react-icons/io5";
|
import { IoBusinessOutline } from "react-icons/io5";
|
||||||
import { RiSurveyLine } from "react-icons/ri";
|
import { RiSurveyLine } from "react-icons/ri";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import {
|
import { selectRecentItems, selectSelectedHeader } from "../../redux/application/application.selectors";
|
||||||
selectRecentItems,
|
|
||||||
selectSelectedHeader
|
|
||||||
} from "../../redux/application/application.selectors";
|
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { signOutStart } from "../../redux/user/user.actions";
|
import { signOutStart } from "../../redux/user/user.actions";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
@@ -64,28 +55,43 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({
|
setBillEnterContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "billEnter"
|
modal: "billEnter"
|
||||||
})),
|
})
|
||||||
setTimeTicketContext: (context) => dispatch(setModalContext({
|
),
|
||||||
|
setTimeTicketContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "timeTicket"
|
modal: "timeTicket"
|
||||||
})),
|
})
|
||||||
|
),
|
||||||
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||||
setReportCenterContext: (context) => dispatch(setModalContext({
|
setReportCenterContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "reportCenter"
|
modal: "reportCenter"
|
||||||
})),
|
})
|
||||||
|
),
|
||||||
signOutStart: () => dispatch(signOutStart()),
|
signOutStart: () => dispatch(signOutStart()),
|
||||||
setCardPaymentContext: (context) => dispatch(setModalContext({
|
setCardPaymentContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "cardPayment"
|
modal: "cardPayment"
|
||||||
})),
|
})
|
||||||
setTaskUpsertContext: (context) => dispatch(setModalContext({
|
),
|
||||||
|
setTaskUpsertContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: 'taskUpsert'
|
modal: "taskUpsert"
|
||||||
})),
|
})
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
function Header({
|
function Header({
|
||||||
@@ -100,7 +106,7 @@ function Header({
|
|||||||
setReportCenterContext,
|
setReportCenterContext,
|
||||||
recentItems,
|
recentItems,
|
||||||
setCardPaymentContext,
|
setCardPaymentContext,
|
||||||
setTaskUpsertContext,
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
treatments: { ImEXPay, DmsAp, Simple_Inventory }
|
treatments: { ImEXPay, DmsAp, Simple_Inventory }
|
||||||
@@ -258,8 +264,7 @@ function Header({
|
|||||||
const accountingExportChildren = [
|
const accountingExportChildren = [
|
||||||
{
|
{
|
||||||
key: "receivables",
|
key: "receivables",
|
||||||
label: <Link
|
label: <Link to="/manage/accounting/receivables">{t("menus.header.accounting-receivables")}</Link>
|
||||||
to="/manage/accounting/receivables">{t("menus.header.accounting-receivables")}</Link>
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -429,14 +434,12 @@ function Header({
|
|||||||
{
|
{
|
||||||
key: "contracts",
|
key: "contracts",
|
||||||
icon: <FileFilled />,
|
icon: <FileFilled />,
|
||||||
label: <Link
|
label: <Link to="/manage/courtesycars/contracts">{t("menus.header.courtesycars-contracts")}</Link>
|
||||||
to="/manage/courtesycars/contracts">{t("menus.header.courtesycars-contracts")}</Link>
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "newcontract",
|
key: "newcontract",
|
||||||
icon: <FileAddFilled />,
|
icon: <FileAddFilled />,
|
||||||
label: <Link
|
label: <Link to="/manage/courtesycars/contracts/new">{t("menus.header.courtesycars-newcontract")}</Link>
|
||||||
to="/manage/courtesycars/contracts/new">{t("menus.header.courtesycars-newcontract")}</Link>
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -472,36 +475,36 @@ function Header({
|
|||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
{
|
{
|
||||||
key: 'tasks',
|
key: "tasks",
|
||||||
id: 'tasks',
|
id: "tasks",
|
||||||
icon: <FaTasks />,
|
icon: <FaTasks />,
|
||||||
label: t('menus.header.tasks'),
|
label: t("menus.header.tasks"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: 'createTask',
|
key: "createTask",
|
||||||
icon: <PlusCircleOutlined />,
|
icon: <PlusCircleOutlined />,
|
||||||
label: t('menus.header.create_task'),
|
label: t("menus.header.create_task"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setTaskUpsertContext({
|
setTaskUpsertContext({
|
||||||
actions: {},
|
actions: {},
|
||||||
context: {},
|
context: {}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'mytasks',
|
key: "mytasks",
|
||||||
icon: <FaTasks />,
|
icon: <FaTasks />,
|
||||||
label: <Link to="/manage/tasks/mytasks">{t('menus.header.my_tasks')}</Link>,
|
label: <Link to="/manage/tasks/mytasks">{t("menus.header.my_tasks")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'all_tasks',
|
key: "all_tasks",
|
||||||
icon: <FaTasks />,
|
icon: <FaTasks />,
|
||||||
label: <Link to="/manage/tasks/alltasks">{t('menus.header.all_tasks')}</Link>,
|
label: <Link to="/manage/tasks/alltasks">{t("menus.header.all_tasks")}</Link>
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'shopsubmenu',
|
key: "shopsubmenu",
|
||||||
icon: <SettingOutlined />,
|
icon: <SettingOutlined />,
|
||||||
label: t("menus.header.shop"),
|
label: t("menus.header.shop"),
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -115,8 +115,7 @@ export function JobLinesExpander({jobline, jobid, bodyshop, currentUser}) {
|
|||||||
key: line.id,
|
key: line.id,
|
||||||
children: (
|
children: (
|
||||||
<Space split={<Divider type="vertical" />} wrap>
|
<Space split={<Divider type="vertical" />} wrap>
|
||||||
<Link
|
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>{line.parts_dispatch.number}</Link>
|
||||||
to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>{line.parts_dispatch.number}</Link>
|
|
||||||
{bodyshop.employees.find((e) => e.id === line.parts_dispatch.employeeid)?.first_name}
|
{bodyshop.employees.find((e) => e.id === line.parts_dispatch.employeeid)?.first_name}
|
||||||
<Space>
|
<Space>
|
||||||
{t("parts_dispatch_lines.fields.accepted_at")}
|
{t("parts_dispatch_lines.fields.accepted_at")}
|
||||||
@@ -133,10 +132,15 @@ export function JobLinesExpander({jobline, jobid, bodyshop, currentUser}) {
|
|||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={24} lg={24}>
|
<Col md={24} lg={24}>
|
||||||
<TaskListContainer currentUser={currentUser} bodyshop={bodyshop} parentJobId={jobid}
|
<TaskListContainer
|
||||||
relationshipType={'joblineid'} relationshipId={jobline.id}
|
currentUser={currentUser}
|
||||||
|
bodyshop={bodyshop}
|
||||||
|
parentJobId={jobid}
|
||||||
|
relationshipType={"joblineid"}
|
||||||
|
relationshipId={jobline.id}
|
||||||
query={QUERY_JOBLINE_TASKS_PAGINATED}
|
query={QUERY_JOBLINE_TASKS_PAGINATED}
|
||||||
titleTranslation='tasks.titles.job_tasks'/>
|
titleTranslation="tasks.titles.job_tasks"
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
setJobLineEditContext: (context) => dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
|
setJobLineEditContext: (context) => dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
|
||||||
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||||
setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})),
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobLinesComponent({
|
export function JobLinesComponent({
|
||||||
@@ -334,14 +334,17 @@ export function JobLinesComponent({
|
|||||||
>
|
>
|
||||||
<EditFilled />
|
<EditFilled />
|
||||||
</Button>
|
</Button>
|
||||||
<Button title={t('tasks.buttons.create')} onClick={() => {
|
<Button
|
||||||
|
title={t("tasks.buttons.create")}
|
||||||
|
onClick={() => {
|
||||||
setTaskUpsertContext({
|
setTaskUpsertContext({
|
||||||
context: {
|
context: {
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
joblineid: record.id
|
joblineid: record.id
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<FaTasks />
|
<FaTasks />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -4,38 +4,23 @@ import { Select, Space, Spin, Tag } from "antd";
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { forwardRef, useEffect, useState } from "react";
|
import React, { forwardRef, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import { SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE, SEARCH_JOBS_FOR_AUTOCOMPLETE } from "../../graphql/jobs.queries";
|
||||||
SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE,
|
|
||||||
SEARCH_JOBS_FOR_AUTOCOMPLETE
|
|
||||||
} from "../../graphql/jobs.queries";
|
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
const JobSearchSelect = (
|
const JobSearchSelect = (
|
||||||
{
|
{ disabled, convertedOnly = false, notInvoiced = false, notExported = true, clm_no = false, ...restProps },
|
||||||
disabled,
|
|
||||||
convertedOnly = false,
|
|
||||||
notInvoiced = false,
|
|
||||||
notExported = true,
|
|
||||||
clm_no = false,
|
|
||||||
...restProps
|
|
||||||
},
|
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [theOptions, setTheOptions] = useState([]);
|
const [theOptions, setTheOptions] = useState([]);
|
||||||
const [callSearch, { loading, error, data }] = useLazyQuery(SEARCH_JOBS_FOR_AUTOCOMPLETE, {});
|
const [callSearch, { loading, error, data }] = useLazyQuery(SEARCH_JOBS_FOR_AUTOCOMPLETE, {});
|
||||||
|
|
||||||
const [
|
const [callIdSearch, { loading: idLoading, error: idError, data: idData }] = useLazyQuery(
|
||||||
callIdSearch,
|
SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE
|
||||||
{
|
);
|
||||||
loading: idLoading,
|
|
||||||
error: idError,
|
|
||||||
data: idData
|
|
||||||
}
|
|
||||||
] = useLazyQuery(SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE);
|
|
||||||
|
|
||||||
const executeSearch = (v) => {
|
const executeSearch = (v) => {
|
||||||
if (v && v.variables?.search !== "" && v.variables.search.length >= 2) callSearch(v);
|
if (v && v.variables?.search !== "" && v.variables.search.length >= 2) callSearch(v);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const data = useMemo(() => {
|
const data = useMemo(() => {
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: t("jobs.labels.subtotal"),
|
key: t("jobs.labels.subtotal"),
|
||||||
|
|||||||
@@ -1,28 +1,13 @@
|
|||||||
import { DownCircleFilled } from "@ant-design/icons";
|
import { DownCircleFilled } from "@ant-design/icons";
|
||||||
import { useApolloClient, useMutation } from "@apollo/client";
|
import { useApolloClient, useMutation } from "@apollo/client";
|
||||||
import {
|
import { Button, Card, Dropdown, Form, Input, Modal, notification, Popconfirm, Popover, Select, Space } from "antd";
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
Dropdown,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
Modal,
|
|
||||||
notification,
|
|
||||||
Popconfirm,
|
|
||||||
Popover,
|
|
||||||
Select,
|
|
||||||
Space
|
|
||||||
} from "antd";
|
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import {
|
import { CANCEL_APPOINTMENTS_BY_JOB_ID, INSERT_MANUAL_APPT } from "../../graphql/appointments.queries";
|
||||||
CANCEL_APPOINTMENTS_BY_JOB_ID,
|
|
||||||
INSERT_MANUAL_APPT
|
|
||||||
} from "../../graphql/appointments.queries";
|
|
||||||
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
|
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
@@ -44,8 +29,7 @@ import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time
|
|||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import JobsDetailHeaderActionsToggleProduction
|
import JobsDetailHeaderActionsToggleProduction from "./jobs-detail-header-actions.toggle-production";
|
||||||
from "./jobs-detail-header-actions.toggle-production";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -55,32 +39,50 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setScheduleContext: (context) => dispatch(setModalContext({ context: context, modal: "schedule" })),
|
setScheduleContext: (context) => dispatch(setModalContext({ context: context, modal: "schedule" })),
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({
|
setBillEnterContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "billEnter"
|
modal: "billEnter"
|
||||||
})),
|
})
|
||||||
|
),
|
||||||
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||||
setJobCostingContext: (context) => dispatch(setModalContext({
|
setJobCostingContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "jobCosting"
|
modal: "jobCosting"
|
||||||
})),
|
})
|
||||||
setTimeTicketContext: (context) => dispatch(setModalContext({
|
),
|
||||||
|
setTimeTicketContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "timeTicket"
|
modal: "timeTicket"
|
||||||
})),
|
})
|
||||||
setCardPaymentContext: (context) => dispatch(setModalContext({
|
),
|
||||||
|
setCardPaymentContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "cardPayment"
|
modal: "cardPayment"
|
||||||
})),
|
})
|
||||||
insertAuditTrail: ({jobid, operation, type}) => dispatch(insertAuditTrail({
|
),
|
||||||
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
|
dispatch(
|
||||||
|
insertAuditTrail({
|
||||||
jobid,
|
jobid,
|
||||||
operation,
|
operation,
|
||||||
type
|
type
|
||||||
})),
|
})
|
||||||
setTimeTicketTaskContext: (context) => dispatch(setModalContext({
|
),
|
||||||
|
setTimeTicketTaskContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "timeTicketTask"
|
modal: "timeTicketTask"
|
||||||
})),
|
})
|
||||||
|
),
|
||||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||||
setMessage: (text) => dispatch(setMessage(text))
|
setMessage: (text) => dispatch(setMessage(text))
|
||||||
@@ -680,8 +682,7 @@ export function JobsDetailHeaderActions({
|
|||||||
{
|
{
|
||||||
key: "checklist",
|
key: "checklist",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: <Link
|
label: <Link to={`/manage/jobs/${job.id}/checklist`}>{t("jobs.actions.viewchecklist")}</Link>
|
||||||
to={`/manage/jobs/${job.id}/checklist`}>{t("jobs.actions.viewchecklist")}</Link>
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
rome: "USE_IMEX",
|
rome: "USE_IMEX",
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import {
|
import { BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined, SyncOutlined } from "@ant-design/icons";
|
||||||
BranchesOutlined,
|
|
||||||
ExclamationCircleFilled,
|
|
||||||
PauseCircleOutlined,
|
|
||||||
SyncOutlined
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
|
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
|||||||
@@ -19,12 +19,10 @@ import {DateFormatter} from "../../utils/DateFormatter";
|
|||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import DataLabel from "../data-label/data-label.component";
|
import DataLabel from "../data-label/data-label.component";
|
||||||
import PartsOrderBackorderEta
|
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
|
||||||
from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
|
|
||||||
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
|
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
|
||||||
import PartsOrderDeleteLine from "../parts-order-delete-line/parts-order-delete-line.component";
|
import PartsOrderDeleteLine from "../parts-order-delete-line/parts-order-delete-line.component";
|
||||||
import PartsOrderLineBackorderButton
|
import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
|
||||||
from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
|
|
||||||
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
|
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
|
||||||
import PrintWrapper from "../print-wrapper/print-wrapper.component";
|
import PrintWrapper from "../print-wrapper/print-wrapper.component";
|
||||||
import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component";
|
import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component";
|
||||||
@@ -32,19 +30,25 @@ import {FaTasks} from "react-icons/fa";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({
|
setBillEnterContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "billEnter"
|
modal: "billEnter"
|
||||||
})),
|
})
|
||||||
setPartsReceiveContext: (context) => dispatch(setModalContext({
|
),
|
||||||
|
setPartsReceiveContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "partsReceive"
|
modal: "partsReceive"
|
||||||
})),
|
})
|
||||||
setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})),
|
),
|
||||||
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function PartsOrderListTableComponent({
|
export function PartsOrderListTableComponent({
|
||||||
@@ -55,7 +59,7 @@ export function PartsOrderListTableComponent({
|
|||||||
billsQuery,
|
billsQuery,
|
||||||
handleOnRowClick,
|
handleOnRowClick,
|
||||||
setPartsReceiveContext,
|
setPartsReceiveContext,
|
||||||
setTaskUpsertContext,
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||||
.filter((screen) => !!screen[1])
|
.filter((screen) => !!screen[1])
|
||||||
@@ -87,7 +91,7 @@ export function PartsOrderListTableComponent({
|
|||||||
const { refetch } = billsQuery;
|
const { refetch } = billsQuery;
|
||||||
|
|
||||||
const recordActions = (record, showView = false) => (
|
const recordActions = (record, showView = false) => (
|
||||||
<Space direction='horizontal' wrap>
|
<Space direction="horizontal" wrap>
|
||||||
{showView && (
|
{showView && (
|
||||||
<Button onClick={() => handleOnRowClick(record)}>
|
<Button onClick={() => handleOnRowClick(record)}>
|
||||||
<EyeFilled />
|
<EyeFilled />
|
||||||
@@ -119,14 +123,17 @@ export function PartsOrderListTableComponent({
|
|||||||
>
|
>
|
||||||
{t("parts_orders.actions.receive")}
|
{t("parts_orders.actions.receive")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button title={t('tasks.buttons.create')} onClick={() => {
|
<Button
|
||||||
|
title={t("tasks.buttons.create")}
|
||||||
|
onClick={() => {
|
||||||
setTaskUpsertContext({
|
setTaskUpsertContext({
|
||||||
context: {
|
context: {
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
partsorderid: record.id
|
partsorderid: record.id
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<FaTasks />
|
<FaTasks />
|
||||||
</Button>
|
</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
@@ -388,8 +395,7 @@ export function PartsOrderListTableComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageHeader title={record && `${record.vendor.name} - ${record.order_number}`}
|
<PageHeader title={record && `${record.vendor.name} - ${record.order_number}`} extra={recordActions(record)} />
|
||||||
extra={recordActions(record)}/>
|
|
||||||
<Table
|
<Table
|
||||||
scroll={{
|
scroll={{
|
||||||
x: true //y: "50rem"
|
x: true //y: "50rem"
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import {
|
|||||||
SyncOutlined
|
SyncOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter.jsx";
|
import { DateFormatter } from "../../utils/DateFormatter.jsx";
|
||||||
import {connect} from 'react-redux';
|
import { connect } from "react-redux";
|
||||||
import {setModalContext} from '../../redux/modals/modals.actions';
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task List Component
|
* Task List Component
|
||||||
@@ -33,11 +33,11 @@ const DueDateRecord = ({dueDate}) => {
|
|||||||
const isBeforeToday = dueDateDayjs.isBefore(dayjs());
|
const isBeforeToday = dueDateDayjs.isBefore(dayjs());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div title={relativeDueDate} style={{color: isBeforeToday ? 'red' : 'black'}}>
|
<div title={relativeDueDate} style={{ color: isBeforeToday ? "red" : "black" }}>
|
||||||
<DateFormatter>{dueDate}</DateFormatter>
|
<DateFormatter>{dueDate}</DateFormatter>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Priority Label Component
|
* Priority Label Component
|
||||||
@@ -48,27 +48,35 @@ const DueDateRecord = ({dueDate}) => {
|
|||||||
const PriorityLabel = ({ priority }) => {
|
const PriorityLabel = ({ priority }) => {
|
||||||
switch (priority) {
|
switch (priority) {
|
||||||
case 1:
|
case 1:
|
||||||
return <div>
|
return (
|
||||||
High <ExclamationCircleFilled style={{marginLeft: '5px', color: 'red'}}/>
|
<div>
|
||||||
</div>;
|
High <ExclamationCircleFilled style={{ marginLeft: "5px", color: "red" }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return <div>
|
return (
|
||||||
Medium <ExclamationCircleFilled style={{marginLeft: '5px', color: 'yellow'}}/>
|
<div>
|
||||||
</div>;
|
Medium <ExclamationCircleFilled style={{ marginLeft: "5px", color: "yellow" }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return <div>
|
return (
|
||||||
Low <ExclamationCircleFilled style={{marginLeft: '5px', color: 'green'}}/>
|
<div>
|
||||||
</div>;
|
Low <ExclamationCircleFilled style={{ marginLeft: "5px", color: "green" }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return <div>
|
return (
|
||||||
None <ExclamationCircleFilled style={{marginLeft: '5px', color: 'black'}}/>
|
<div>
|
||||||
</div>;
|
None <ExclamationCircleFilled style={{ marginLeft: "5px", color: "black" }} />
|
||||||
}
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
// Existing dispatch props...
|
// Existing dispatch props...
|
||||||
setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})),
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => ({
|
||||||
@@ -91,56 +99,46 @@ function TaskListComponent({
|
|||||||
relationshipId,
|
relationshipId,
|
||||||
onlyMine,
|
onlyMine,
|
||||||
parentJobId,
|
parentJobId,
|
||||||
showRo = true,
|
showRo = true
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
|
|
||||||
// Extract Query Params
|
// Extract Query Params
|
||||||
const {
|
const { page, sortcolumn, sortorder, deleted, completed, mine } = search;
|
||||||
page,
|
|
||||||
sortcolumn,
|
|
||||||
sortorder,
|
|
||||||
deleted,
|
|
||||||
completed,
|
|
||||||
mine
|
|
||||||
} = search;
|
|
||||||
|
|
||||||
const history = useNavigate();
|
const history = useNavigate();
|
||||||
const columns = [];
|
const columns = [];
|
||||||
|
|
||||||
if (!onlyMine) {
|
if (!onlyMine) {
|
||||||
columns.push(
|
columns.push({
|
||||||
{
|
|
||||||
title: t("tasks.fields.assigned_to"),
|
title: t("tasks.fields.assigned_to"),
|
||||||
dataIndex: "assigned_to",
|
dataIndex: "assigned_to",
|
||||||
key: "assigned_to",
|
key: "assigned_to",
|
||||||
width: '8%',
|
width: "8%",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
sortOrder: sortcolumn === "assigned_to" && sortorder,
|
sortOrder: sortcolumn === "assigned_to" && sortorder,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
const employee = bodyshop?.employees?.find(e => e.user_email === record.assigned_to);
|
const employee = bodyshop?.employees?.find((e) => e.user_email === record.assigned_to);
|
||||||
return employee ? `${employee.first_name} ${employee.last_name}` : t("general.labels.na");
|
return employee ? `${employee.first_name} ${employee.last_name}` : t("general.labels.na");
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showRo) {
|
if (showRo) {
|
||||||
columns.push(
|
columns.push({
|
||||||
{
|
|
||||||
title: t("tasks.fields.job.ro_number"),
|
title: t("tasks.fields.job.ro_number"),
|
||||||
dataIndex: ["job", "ro_number"],
|
dataIndex: ["job", "ro_number"],
|
||||||
key: "job.ro_number",
|
key: "job.ro_number",
|
||||||
width: '8%',
|
width: "8%",
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.job
|
record.job ? (
|
||||||
? <Link
|
<Link to={`/manage/jobs/${record.job.id}`}>{record.job.ro_number || t("general.labels.na")}</Link>
|
||||||
to={`/manage/jobs/${record.job.id}`}>{record.job.ro_number || t("general.labels.na")}</Link>
|
) : (
|
||||||
: t("general.labels.na")
|
t("general.labels.na")
|
||||||
}
|
)
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
columns.push(
|
columns.push(
|
||||||
@@ -148,44 +146,47 @@ function TaskListComponent({
|
|||||||
title: t("tasks.fields.jobline"),
|
title: t("tasks.fields.jobline"),
|
||||||
dataIndex: ["jobline", "id"],
|
dataIndex: ["jobline", "id"],
|
||||||
key: "jobline.id",
|
key: "jobline.id",
|
||||||
width: '8%',
|
width: "8%",
|
||||||
render: (text, record) => record?.jobline?.line_desc || ''
|
render: (text, record) => record?.jobline?.line_desc || ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("tasks.fields.parts_order"),
|
title: t("tasks.fields.parts_order"),
|
||||||
dataIndex: ["parts_order", "id"],
|
dataIndex: ["parts_order", "id"],
|
||||||
key: "part_order.id",
|
key: "part_order.id",
|
||||||
width: '8%',
|
width: "8%",
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.parts_order
|
record.parts_order ? (
|
||||||
? <Link
|
<Link to={`/manage/jobs/${record.job.id}?partsorderid=${record.parts_order.id}&tab=partssublet`}>
|
||||||
to={`/manage/jobs/${record.job.id}?partsorderid=${record.parts_order.id}&tab=partssublet`}>
|
|
||||||
{record.parts_order.order_number && record.parts_order.vendor && record.parts_order.vendor.name
|
{record.parts_order.order_number && record.parts_order.vendor && record.parts_order.vendor.name
|
||||||
? `${record.parts_order.order_number} - ${record.parts_order.vendor.name}`
|
? `${record.parts_order.order_number} - ${record.parts_order.vendor.name}`
|
||||||
: t("general.labels.na")}
|
: t("general.labels.na")}
|
||||||
</Link>
|
</Link>
|
||||||
: ''
|
) : (
|
||||||
|
""
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("tasks.fields.bill"),
|
title: t("tasks.fields.bill"),
|
||||||
dataIndex: ["bill", "id"],
|
dataIndex: ["bill", "id"],
|
||||||
key: "bill.id",
|
key: "bill.id",
|
||||||
width: '8%',
|
width: "8%",
|
||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.bill
|
record.bill ? (
|
||||||
? <Link to={`/manage/jobs/${record.job.id}?billid=${record.bill.id}&tab=partssublet`}>
|
<Link to={`/manage/jobs/${record.job.id}?billid=${record.bill.id}&tab=partssublet`}>
|
||||||
{record.bill.invoice_number && record.bill.vendor && record.bill.vendor.name
|
{record.bill.invoice_number && record.bill.vendor && record.bill.vendor.name
|
||||||
? `${record.bill.invoice_number} - ${record.bill.vendor.name}`
|
? `${record.bill.invoice_number} - ${record.bill.vendor.name}`
|
||||||
: t("general.labels.na")}
|
: t("general.labels.na")}
|
||||||
</Link>
|
</Link>
|
||||||
: ''
|
) : (
|
||||||
|
""
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("tasks.fields.title"),
|
title: t("tasks.fields.title"),
|
||||||
dataIndex: "title",
|
dataIndex: "title",
|
||||||
key: "title",
|
key: "title",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
sortOrder: sortcolumn === "title" && sortorder,
|
sortOrder: sortcolumn === "title" && sortorder
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("tasks.fields.due_date"),
|
title: t("tasks.fields.due_date"),
|
||||||
@@ -193,8 +194,8 @@ function TaskListComponent({
|
|||||||
key: "due_date",
|
key: "due_date",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
sortOrder: sortcolumn === "due_date" && sortorder,
|
sortOrder: sortcolumn === "due_date" && sortorder,
|
||||||
width: '8%',
|
width: "8%",
|
||||||
render: (text, record) => <DueDateRecord dueDate={record.due_date}/>,
|
render: (text, record) => <DueDateRecord dueDate={record.due_date} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("tasks.fields.remind_at"),
|
title: t("tasks.fields.remind_at"),
|
||||||
@@ -202,8 +203,8 @@ function TaskListComponent({
|
|||||||
key: "remind_at",
|
key: "remind_at",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
sortOrder: sortcolumn === "remind_at" && sortorder,
|
sortOrder: sortcolumn === "remind_at" && sortorder,
|
||||||
width: '8%',
|
width: "8%",
|
||||||
render: (text, record) => <DueDateRecord dueDate={record.remind_at}/>,
|
render: (text, record) => <DueDateRecord dueDate={record.remind_at} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("tasks.fields.priority"),
|
title: t("tasks.fields.priority"),
|
||||||
@@ -211,42 +212,44 @@ function TaskListComponent({
|
|||||||
key: "priority",
|
key: "priority",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
sortOrder: sortcolumn === "priority" && sortorder,
|
sortOrder: sortcolumn === "priority" && sortorder,
|
||||||
width: '8%',
|
width: "8%",
|
||||||
render: (text, record) => <PriorityLabel priority={record.priority}
|
render: (text, record) => <PriorityLabel priority={record.priority} />
|
||||||
/>
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("tasks.fields.actions"),
|
title: t("tasks.fields.actions"),
|
||||||
key: "toggleCompleted",
|
key: "toggleCompleted",
|
||||||
width: '5%',
|
width: "5%",
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Space direction='horizontal'>
|
<Space direction="horizontal">
|
||||||
<Button title={t('tasks.buttons.edit')} onClick={() => {
|
<Button
|
||||||
|
title={t("tasks.buttons.edit")}
|
||||||
|
onClick={() => {
|
||||||
setTaskUpsertContext({
|
setTaskUpsertContext({
|
||||||
context: {
|
context: {
|
||||||
existingTask: record,
|
existingTask: record
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<EditFilled />
|
<EditFilled />
|
||||||
</Button>
|
</Button>
|
||||||
<Button title={t('tasks.buttons.complete')}
|
<Button
|
||||||
onClick={() => toggleCompletedStatus(record.id, record.completed)}>
|
title={t("tasks.buttons.complete")}
|
||||||
{record.completed ? <CheckCircleOutlined/> :
|
onClick={() => toggleCompletedStatus(record.id, record.completed)}
|
||||||
<CheckCircleFilled/>}
|
>
|
||||||
|
{record.completed ? <CheckCircleOutlined /> : <CheckCircleFilled />}
|
||||||
</Button>
|
</Button>
|
||||||
<Button title={t('tasks.buttons.delete')}
|
<Button title={t("tasks.buttons.delete")} onClick={() => toggleDeletedStatus(record.id, record.deleted)}>
|
||||||
onClick={() => toggleDeletedStatus(record.id, record.deleted)}>
|
|
||||||
{record.deleted ? <DeleteFilled /> : <DeleteOutlined />}
|
{record.deleted ? <DeleteFilled /> : <DeleteOutlined />}
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
),
|
)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
filteredInfo: {text: ""},
|
filteredInfo: { text: "" }
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCreateTask = useCallback(() => {
|
const handleCreateTask = useCallback(() => {
|
||||||
@@ -254,8 +257,8 @@ function TaskListComponent({
|
|||||||
actions: {},
|
actions: {},
|
||||||
context: {
|
context: {
|
||||||
jobid: parentJobId,
|
jobid: parentJobId,
|
||||||
[relationshipType]: relationshipId,
|
[relationshipType]: relationshipId
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}, [parentJobId, relationshipId, relationshipType, setTaskUpsertContext]);
|
}, [parentJobId, relationshipId, relationshipType, setTaskUpsertContext]);
|
||||||
|
|
||||||
@@ -267,19 +270,24 @@ function TaskListComponent({
|
|||||||
history({ search: queryString.stringify(search) });
|
history({ search: queryString.stringify(search) });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSwitchChange = useCallback((param, value) => {
|
const handleSwitchChange = useCallback(
|
||||||
|
(param, value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
search[param] = "true";
|
search[param] = "true";
|
||||||
} else {
|
} else {
|
||||||
delete search[param];
|
delete search[param];
|
||||||
}
|
}
|
||||||
history({ search: queryString.stringify(search) });
|
history({ search: queryString.stringify(search) });
|
||||||
}, [history, search]);
|
},
|
||||||
|
[history, search]
|
||||||
|
);
|
||||||
|
|
||||||
const expandableRow = (record) => {
|
const expandableRow = (record) => {
|
||||||
return <Card title={t('tasks.fields.description')} size='small'>
|
return (
|
||||||
|
<Card title={t("tasks.fields.description")} size="small">
|
||||||
{record.description}
|
{record.description}
|
||||||
</Card>
|
</Card>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -288,35 +296,35 @@ function TaskListComponent({
|
|||||||
*/
|
*/
|
||||||
const tasksExtra = useCallback(() => {
|
const tasksExtra = useCallback(() => {
|
||||||
return (
|
return (
|
||||||
<Space direction='horizontal'>
|
<Space direction="horizontal">
|
||||||
{!onlyMine && (
|
{!onlyMine && (
|
||||||
<Switch
|
<Switch
|
||||||
checkedChildren={t('tasks.buttons.myTasks')}
|
checkedChildren={t("tasks.buttons.myTasks")}
|
||||||
unCheckedChildren={t('tasks.buttons.allTasks')}
|
unCheckedChildren={t("tasks.buttons.allTasks")}
|
||||||
title={t('tasks.titles.mine')}
|
title={t("tasks.titles.mine")}
|
||||||
checked={mine === "true"}
|
checked={mine === "true"}
|
||||||
onChange={(value) => handleSwitchChange('mine', value)}
|
onChange={(value) => handleSwitchChange("mine", value)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Switch
|
<Switch
|
||||||
checkedChildren={<CheckCircleFilled />}
|
checkedChildren={<CheckCircleFilled />}
|
||||||
unCheckedChildren={<CheckCircleOutlined />}
|
unCheckedChildren={<CheckCircleOutlined />}
|
||||||
title={t('tasks.titles.completed')}
|
title={t("tasks.titles.completed")}
|
||||||
checked={completed === "true"}
|
checked={completed === "true"}
|
||||||
onChange={(value) => handleSwitchChange('completed', value)}
|
onChange={(value) => handleSwitchChange("completed", value)}
|
||||||
/>
|
/>
|
||||||
<Switch
|
<Switch
|
||||||
checkedChildren={<DeleteOutlined />}
|
checkedChildren={<DeleteOutlined />}
|
||||||
unCheckedChildren={<DeleteFilled />}
|
unCheckedChildren={<DeleteFilled />}
|
||||||
title={t('tasks.titles.deleted')}
|
title={t("tasks.titles.deleted")}
|
||||||
checked={deleted === "true"}
|
checked={deleted === "true"}
|
||||||
onChange={(value) => handleSwitchChange('deleted', value)}
|
onChange={(value) => handleSwitchChange("deleted", value)}
|
||||||
/>
|
/>
|
||||||
<Button title={t('tasks.buttons.create')} onClick={handleCreateTask}>
|
<Button title={t("tasks.buttons.create")} onClick={handleCreateTask}>
|
||||||
<PlusCircleFilled/>{t('tasks.buttons.create')}
|
<PlusCircleFilled />
|
||||||
|
{t("tasks.buttons.create")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button title={t('tasks.buttons.refresh')}
|
<Button title={t("tasks.buttons.refresh")} onClick={() => refetch()}>
|
||||||
onClick={() => refetch()}>
|
|
||||||
<SyncOutlined />
|
<SyncOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
@@ -324,10 +332,7 @@ function TaskListComponent({
|
|||||||
}, [refetch, deleted, completed, mine, onlyMine, t, handleSwitchChange, handleCreateTask]);
|
}, [refetch, deleted, completed, mine, onlyMine, t, handleSwitchChange, handleCreateTask]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card title={titleTranslation} extra={tasksExtra()}>
|
||||||
title={titleTranslation}
|
|
||||||
extra={tasksExtra()}
|
|
||||||
>
|
|
||||||
<Table
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
pagination={{
|
pagination={{
|
||||||
@@ -335,7 +340,7 @@ function TaskListComponent({
|
|||||||
current: parseInt(page || 1),
|
current: parseInt(page || 1),
|
||||||
total: total,
|
total: total,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
showQuickJumper: true,
|
showQuickJumper: true
|
||||||
}}
|
}}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
@@ -344,7 +349,7 @@ function TaskListComponent({
|
|||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
expandable={{
|
expandable={{
|
||||||
expandedRowRender: expandableRow,
|
expandedRowRender: expandableRow,
|
||||||
rowExpandable: record => record.description,
|
rowExpandable: (record) => record.description
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { useMutation, useQuery } from "@apollo/client";
|
import { useMutation, useQuery } from "@apollo/client";
|
||||||
import {
|
import { MUTATION_TOGGLE_TASK_COMPLETED, MUTATION_TOGGLE_TASK_DELETED } from "../../graphql/tasks.queries.js";
|
||||||
MUTATION_TOGGLE_TASK_COMPLETED,
|
|
||||||
MUTATION_TOGGLE_TASK_DELETED,
|
|
||||||
} from "../../graphql/tasks.queries.js";
|
|
||||||
import { pageLimit } from "../../utils/config.js";
|
import { pageLimit } from "../../utils/config.js";
|
||||||
import AlertComponent from "../alert/alert.component.jsx";
|
import AlertComponent from "../alert/alert.component.jsx";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
@@ -30,44 +27,37 @@ export default function TaskListContainer({
|
|||||||
const searchParams = queryString.parse(useLocation().search);
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
const { page, sortcolumn, sortorder, deleted, completed, mine } = searchParams;
|
const { page, sortcolumn, sortorder, deleted, completed, mine } = searchParams;
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const {loading, error, data, refetch} = useQuery(
|
const { loading, error, data, refetch } = useQuery(query, {
|
||||||
query,
|
|
||||||
{
|
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
variables: {
|
variables: {
|
||||||
bodyshop: bodyshop.id,
|
bodyshop: bodyshop.id,
|
||||||
[relationshipType]: relationshipId,
|
[relationshipType]: relationshipId,
|
||||||
deleted: deleted === 'true',
|
deleted: deleted === "true",
|
||||||
completed: completed === "true",
|
completed: completed === "true",
|
||||||
assigned_to: mine === "true" ? currentUser.email : undefined, // replace currentUserID with the actual ID of the current user
|
assigned_to: mine === "true" ? currentUser.email : undefined, // replace currentUserID with the actual ID of the current user
|
||||||
offset: page ? (page - 1) * pageLimit : 0,
|
offset: page ? (page - 1) * pageLimit : 0,
|
||||||
limit: pageLimit,
|
limit: pageLimit,
|
||||||
order: [
|
order: [
|
||||||
{
|
{
|
||||||
[sortcolumn || "created_at"]: sortorder
|
[sortcolumn || "created_at"]: sortorder ? (sortorder === "descend" ? "desc" : "asc") : "desc"
|
||||||
? sortorder === "descend"
|
|
||||||
? "desc"
|
|
||||||
: "asc"
|
|
||||||
: "desc",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refetch tasks when a task is updated
|
* Refetch tasks when a task is updated
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleTaskUpdated = async (event) => {
|
const handleTaskUpdated = async (event) => {
|
||||||
await refetch().catch(e => `Something went wrong fetching tasks: ${e.message || ''}`);
|
await refetch().catch((e) => `Something went wrong fetching tasks: ${e.message || ""}`);
|
||||||
};
|
};
|
||||||
window.addEventListener('taskUpdated', handleTaskUpdated);
|
window.addEventListener("taskUpdated", handleTaskUpdated);
|
||||||
|
|
||||||
// Clean up the event listener when the component is unmounted.
|
// Clean up the event listener when the component is unmounted.
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('taskUpdated', handleTaskUpdated);
|
window.removeEventListener("taskUpdated", handleTaskUpdated);
|
||||||
};
|
};
|
||||||
}, [refetch]);
|
}, [refetch]);
|
||||||
|
|
||||||
@@ -97,27 +87,25 @@ export default function TaskListContainer({
|
|||||||
dispatch(
|
dispatch(
|
||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: toggledTask.data.update_tasks_by_pk.jobid,
|
jobid: toggledTask.data.update_tasks_by_pk.jobid,
|
||||||
operation: toggledTask?.data?.update_tasks_by_pk?.completed ? AuditTrailMapping.tasksCompleted(
|
operation: toggledTask?.data?.update_tasks_by_pk?.completed
|
||||||
toggledTask.data.update_tasks_by_pk.title,
|
? AuditTrailMapping.tasksCompleted(toggledTask.data.update_tasks_by_pk.title, currentUser.email)
|
||||||
currentUser.email
|
: AuditTrailMapping.tasksUncompleted(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"
|
type: toggledTask?.data?.update_tasks_by_pk?.completed ? "tasksCompleted" : "tasksUncompleted"
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.dispatchEvent(new CustomEvent('taskUpdated', {
|
window.dispatchEvent(
|
||||||
detail: {message: 'A task has been completed.'},
|
new CustomEvent("taskUpdated", {
|
||||||
}));
|
detail: { message: "A task has been completed." }
|
||||||
|
})
|
||||||
|
);
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("tasks.successes.completed"),
|
message: t("tasks.successes.completed")
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("tasks.failures.completed"),
|
message: t("tasks.failures.completed")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -148,32 +136,28 @@ export default function TaskListContainer({
|
|||||||
dispatch(
|
dispatch(
|
||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: toggledTask.data.update_tasks_by_pk.jobid,
|
jobid: toggledTask.data.update_tasks_by_pk.jobid,
|
||||||
operation: toggledTask?.data?.update_tasks_by_pk?.deleted ? AuditTrailMapping.tasksDeleted(
|
operation: toggledTask?.data?.update_tasks_by_pk?.deleted
|
||||||
toggledTask.data.update_tasks_by_pk.title,
|
? AuditTrailMapping.tasksDeleted(toggledTask.data.update_tasks_by_pk.title, currentUser.email)
|
||||||
currentUser.email
|
: AuditTrailMapping.tasksUndeleted(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"
|
type: toggledTask?.data?.update_tasks_by_pk?.deleted ? "tasksDeleted" : "tasksUndeleted"
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.dispatchEvent(
|
||||||
window.dispatchEvent(new CustomEvent('taskUpdated', {
|
new CustomEvent("taskUpdated", {
|
||||||
detail: {message: 'A task has been deleted.'},
|
detail: { message: "A task has been deleted." }
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("tasks.successes.deleted"),
|
message: t("tasks.successes.deleted")
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.dir(err);
|
console.dir(err);
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("tasks.failures.deleted"),
|
message: t("tasks.failures.deleted")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {useTranslation} from "react-i18next";
|
|||||||
import { FormDatePicker } from "../form-date-picker/form-date-picker.component.jsx";
|
import { FormDatePicker } from "../form-date-picker/form-date-picker.component.jsx";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js";
|
||||||
import dayjs from '../../utils/day';
|
import dayjs from "../../utils/day";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component.jsx";
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component.jsx";
|
||||||
@@ -12,16 +12,12 @@ import JobSearchSelectComponent from "../job-search-select/job-search-select.com
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({});
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
export default connect(
|
export default connect(mapStateToProps, mapDispatchToProps)(TaskUpsertModalComponent);
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(TaskUpsertModalComponent);
|
|
||||||
|
|
||||||
|
|
||||||
export function TaskUpsertModalComponent({
|
export function TaskUpsertModalComponent({
|
||||||
form,
|
form,
|
||||||
@@ -31,19 +27,18 @@ export function TaskUpsertModalComponent({
|
|||||||
setSelectedJobId,
|
setSelectedJobId,
|
||||||
selectedJobDetails,
|
selectedJobDetails,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const datePickerPresets = [
|
const datePickerPresets = [
|
||||||
{label: t('tasks.date_presets.today'), value: dayjs()},
|
{ label: t("tasks.date_presets.today"), value: dayjs() },
|
||||||
{label: t('tasks.date_presets.tomorrow'), value: dayjs().add(1, 'day')},
|
{ 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.next_week"), value: dayjs().add(1, "week") },
|
||||||
{label: t('tasks.date_presets.two_weeks'), value: dayjs().add(2, 'weeks')},
|
{ 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.three_weeks"), value: dayjs().add(3, "weeks") },
|
||||||
{label: t('tasks.date_presets.one_month'), value: dayjs().add(1, 'month')},
|
{ label: t("tasks.date_presets.one_month"), value: dayjs().add(1, "month") },
|
||||||
{label: t('tasks.date_presets.three_months'), value: dayjs().add(3, 'month')},
|
{ label: t("tasks.date_presets.three_months"), value: dayjs().add(3, "month") }
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const clearRelations = () => {
|
const clearRelations = () => {
|
||||||
@@ -52,7 +47,7 @@ export function TaskUpsertModalComponent({
|
|||||||
partsorderid: null,
|
partsorderid: null,
|
||||||
joblineid: null
|
joblineid: null
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the selected job id
|
* Change the selected job id
|
||||||
@@ -64,7 +59,6 @@ export function TaskUpsertModalComponent({
|
|||||||
clearRelations();
|
clearRelations();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if (loading || error) return <LoadingSkeleton active />;
|
if (loading || error) return <LoadingSkeleton active />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -76,24 +70,20 @@ export function TaskUpsertModalComponent({
|
|||||||
name="title"
|
name="title"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true
|
||||||
},
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("tasks.fields.title")} />
|
<Input placeholder={t("tasks.fields.title")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={4}>
|
<Col span={4}>
|
||||||
<Form.Item
|
<Form.Item label={t("tasks.fields.priority")} name="priority" initialValue={3}>
|
||||||
label={t("tasks.fields.priority")}
|
|
||||||
name="priority"
|
|
||||||
initialValue={3}
|
|
||||||
>
|
|
||||||
<Select
|
<Select
|
||||||
options={[
|
options={[
|
||||||
{ value: 3, label: t("tasks.fields.priorities.low") },
|
{ value: 3, label: t("tasks.fields.priorities.low") },
|
||||||
{ value: 2, label: t("tasks.fields.priorities.medium") },
|
{ value: 2, label: t("tasks.fields.priorities.medium") },
|
||||||
{value: 1, label: t("tasks.fields.priorities.high")},
|
{ value: 1, label: t("tasks.fields.priorities.high") }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -106,8 +96,8 @@ export function TaskUpsertModalComponent({
|
|||||||
initialValue={false}
|
initialValue={false}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true
|
||||||
},
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
@@ -122,23 +112,27 @@ export function TaskUpsertModalComponent({
|
|||||||
label={t("tasks.fields.jobid")}
|
label={t("tasks.fields.jobid")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true
|
||||||
},
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<JobSearchSelectComponent placeholder={t('tasks.placeholders.jobid')}
|
<JobSearchSelectComponent
|
||||||
onSelect={changeJobId} onClear={changeJobId} autoFocus={false}/>
|
placeholder={t("tasks.placeholders.jobid")}
|
||||||
|
onSelect={changeJobId}
|
||||||
|
onClear={changeJobId}
|
||||||
|
autoFocus={false}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Form.Item
|
<Form.Item label={t("tasks.fields.joblineid")} name="joblineid">
|
||||||
label={t("tasks.fields.joblineid")}
|
<Select
|
||||||
name="joblineid"
|
allowClear
|
||||||
|
placeholder={t("tasks.placeholders.joblineid")}
|
||||||
|
disabled={!selectedJobDetails || !selectedJobId}
|
||||||
>
|
>
|
||||||
<Select allowClear placeholder={t("tasks.placeholders.joblineid")}
|
|
||||||
disabled={!selectedJobDetails || !selectedJobId}>
|
|
||||||
{selectedJobDetails?.joblines?.map((jobline) => (
|
{selectedJobDetails?.joblines?.map((jobline) => (
|
||||||
<Select.Option key={jobline.id} value={jobline.id}>
|
<Select.Option key={jobline.id} value={jobline.id}>
|
||||||
{jobline.line_desc}
|
{jobline.line_desc}
|
||||||
@@ -148,12 +142,12 @@ export function TaskUpsertModalComponent({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Form.Item
|
<Form.Item label={t("tasks.fields.partsorderid")} name="partsorderid">
|
||||||
label={t("tasks.fields.partsorderid")}
|
<Select
|
||||||
name="partsorderid"
|
allowClear
|
||||||
|
placeholder={t("tasks.placeholders.partsorderid")}
|
||||||
|
disabled={!selectedJobDetails || !selectedJobId}
|
||||||
>
|
>
|
||||||
<Select allowClear placeholder={t("tasks.placeholders.partsorderid")}
|
|
||||||
disabled={!selectedJobDetails || !selectedJobId}>
|
|
||||||
{selectedJobDetails?.parts_orders?.map((partsOrder) => (
|
{selectedJobDetails?.parts_orders?.map((partsOrder) => (
|
||||||
<Select.Option key={partsOrder.id} value={partsOrder.id}>
|
<Select.Option key={partsOrder.id} value={partsOrder.id}>
|
||||||
{partsOrder.order_number} - {partsOrder.vendor.name}
|
{partsOrder.order_number} - {partsOrder.vendor.name}
|
||||||
@@ -163,12 +157,12 @@ export function TaskUpsertModalComponent({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Form.Item
|
<Form.Item label={t("tasks.fields.billid")} name="billid">
|
||||||
label={t("tasks.fields.billid")}
|
<Select
|
||||||
name="billid"
|
allowClear
|
||||||
|
placeholder={t("tasks.placeholders.billid")}
|
||||||
|
disabled={!selectedJobDetails || !selectedJobId}
|
||||||
>
|
>
|
||||||
<Select allowClear placeholder={t("tasks.placeholders.billid")}
|
|
||||||
disabled={!selectedJobDetails || !selectedJobId}>
|
|
||||||
{selectedJobDetails?.bills?.map((bill) => (
|
{selectedJobDetails?.bills?.map((bill) => (
|
||||||
<Select.Option key={bill.id} value={bill.id}>
|
<Select.Option key={bill.id} value={bill.id}>
|
||||||
{bill.invoice_number} - {bill.vendor.name}
|
{bill.invoice_number} - {bill.vendor.name}
|
||||||
@@ -186,12 +180,14 @@ export function TaskUpsertModalComponent({
|
|||||||
initialValue={currentUser.email}
|
initialValue={currentUser.email}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true
|
||||||
},
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Select placeholder={t("tasks.placeholders.assigned_to")}>
|
<Select placeholder={t("tasks.placeholders.assigned_to")}>
|
||||||
{bodyshop.employees.filter(x => x.active).map((employee) => (
|
{bodyshop.employees
|
||||||
|
.filter((x) => x.active)
|
||||||
|
.map((employee) => (
|
||||||
<Select.Option key={employee.id} value={employee.user_email}>
|
<Select.Option key={employee.id} value={employee.user_email}>
|
||||||
{employee.first_name} {employee.last_name}
|
{employee.first_name} {employee.last_name}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
@@ -200,32 +196,20 @@ export function TaskUpsertModalComponent({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Form.Item
|
<Form.Item label={t("tasks.fields.due_date")} name="due_date">
|
||||||
label={t("tasks.fields.due_date")}
|
|
||||||
name="due_date"
|
|
||||||
>
|
|
||||||
<FormDatePicker format="MM/DD/YYYY" presets={datePickerPresets} />
|
<FormDatePicker format="MM/DD/YYYY" presets={datePickerPresets} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Form.Item
|
<Form.Item label={t("tasks.fields.remind_at")} name="remind_at">
|
||||||
label={t("tasks.fields.remind_at")}
|
|
||||||
name="remind_at"
|
|
||||||
>
|
|
||||||
<FormDatePicker format="MM/DD/YYYY" presets={datePickerPresets} />
|
<FormDatePicker format="MM/DD/YYYY" presets={datePickerPresets} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Form.Item
|
<Form.Item label={t("tasks.fields.description")} name="description">
|
||||||
label={t("tasks.fields.description")}
|
<Input.TextArea rows={8} placeholder={t("tasks.fields.description")} />
|
||||||
name="description"
|
|
||||||
>
|
|
||||||
<Input.TextArea
|
|
||||||
rows={8}
|
|
||||||
placeholder={t("tasks.fields.description")}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
@@ -4,11 +4,7 @@ import React, {useEffect, useState} from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import {
|
import { MUTATION_INSERT_NEW_TASK, MUTATION_UPDATE_TASK, QUERY_GET_TASK_BY_ID } from "../../graphql/tasks.queries";
|
||||||
MUTATION_INSERT_NEW_TASK,
|
|
||||||
MUTATION_UPDATE_TASK,
|
|
||||||
QUERY_GET_TASK_BY_ID
|
|
||||||
} from "../../graphql/tasks.queries";
|
|
||||||
import { QUERY_GET_TASKS_JOB_DETAILS_BY_ID } from "../../graphql/jobs.queries.js";
|
import { QUERY_GET_TASKS_JOB_DETAILS_BY_ID } from "../../graphql/jobs.queries.js";
|
||||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||||
import { selectTaskUpsert } from "../../redux/modals/modals.selectors";
|
import { selectTaskUpsert } from "../../redux/modals/modals.selectors";
|
||||||
@@ -17,14 +13,14 @@ import TaskUpsertModalComponent from "./task-upsert-modal.component";
|
|||||||
import { replaceUndefinedWithNull } from "../../utils/undefinedtonull.js";
|
import { replaceUndefinedWithNull } from "../../utils/undefinedtonull.js";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import dayjs from '../../utils/day';
|
import dayjs from "../../utils/day";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions.js";
|
import { insertAuditTrail } from "../../redux/application/application.actions.js";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings.js";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings.js";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
taskUpsert: selectTaskUpsert,
|
taskUpsert: selectTaskUpsert
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")),
|
||||||
@@ -32,13 +28,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
dispatch(insertAuditTrail({ jobid, billid, operation, type }))
|
dispatch(insertAuditTrail({ jobid, billid, operation, type }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function TaskUpsertModalContainer({
|
export function TaskUpsertModalContainer({ bodyshop, currentUser, taskUpsert, toggleModalVisible, insertAuditTrail }) {
|
||||||
bodyshop,
|
|
||||||
currentUser,
|
|
||||||
taskUpsert,
|
|
||||||
toggleModalVisible,
|
|
||||||
insertAuditTrail
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useNavigate();
|
const history = useNavigate();
|
||||||
const [insertTask] = useMutation(MUTATION_INSERT_NEW_TASK);
|
const [insertTask] = useMutation(MUTATION_INSERT_NEW_TASK);
|
||||||
@@ -51,13 +41,9 @@ export function TaskUpsertModalContainer({
|
|||||||
const [selectedJobDetails, setSelectedJobDetails] = useState(null);
|
const [selectedJobDetails, setSelectedJobDetails] = useState(null);
|
||||||
const [jobIdState, setJobIdState] = useState(null);
|
const [jobIdState, setJobIdState] = useState(null);
|
||||||
|
|
||||||
const {
|
const { loading, error, data } = useQuery(QUERY_GET_TASKS_JOB_DETAILS_BY_ID, {
|
||||||
loading,
|
|
||||||
error,
|
|
||||||
data
|
|
||||||
} = useQuery(QUERY_GET_TASKS_JOB_DETAILS_BY_ID, {
|
|
||||||
variables: { id: jobIdState },
|
variables: { id: jobIdState },
|
||||||
skip: !jobIdState,
|
skip: !jobIdState
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -66,7 +52,7 @@ export function TaskUpsertModalContainer({
|
|||||||
data: taskData
|
data: taskData
|
||||||
} = useQuery(QUERY_GET_TASK_BY_ID, {
|
} = useQuery(QUERY_GET_TASK_BY_ID, {
|
||||||
variables: { id: taskId },
|
variables: { id: taskId },
|
||||||
skip: !taskId,
|
skip: !taskId
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use Effect to hydrate existing task if only a taskid is provided
|
// Use Effect to hydrate existing task if only a taskid is provided
|
||||||
@@ -109,16 +95,15 @@ export function TaskUpsertModalContainer({
|
|||||||
};
|
};
|
||||||
}, [jobid, existingTask, form, open, joblineid, billid, partsorderid]);
|
}, [jobid, existingTask, form, open, joblineid, billid, partsorderid]);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the taskid from the URL
|
* Remove the taskid from the URL
|
||||||
*/
|
*/
|
||||||
const removeTaskIdFromUrl = () => {
|
const removeTaskIdFromUrl = () => {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
if (!urlParams.has('taskid')) return;
|
if (!urlParams.has("taskid")) return;
|
||||||
urlParams.delete('taskid');
|
urlParams.delete("taskid");
|
||||||
history(`${window.location.pathname}?${urlParams}`);
|
history(`${window.location.pathname}?${urlParams}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle existing task
|
* Handle existing task
|
||||||
@@ -132,7 +117,7 @@ export function TaskUpsertModalContainer({
|
|||||||
variables: {
|
variables: {
|
||||||
taskId: existingTask.id,
|
taskId: existingTask.id,
|
||||||
task: replaceUndefinedWithNull(values)
|
task: replaceUndefinedWithNull(values)
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!taskData.errors) {
|
if (!taskData.errors) {
|
||||||
@@ -140,40 +125,43 @@ export function TaskUpsertModalContainer({
|
|||||||
|
|
||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: oldTask.jobid,
|
jobid: oldTask.jobid,
|
||||||
operation: AuditTrailMapping.tasksUpdated(
|
operation: AuditTrailMapping.tasksUpdated(oldTask.title, currentUser.email),
|
||||||
oldTask.title,
|
|
||||||
currentUser.email
|
|
||||||
),
|
|
||||||
type: "tasksUpdated"
|
type: "tasksUpdated"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAssignedToDirty) {
|
if (isAssignedToDirty) {
|
||||||
// TODO This is being moved serverside
|
// TODO This is being moved serverside
|
||||||
axios.post("/sendemail", {
|
axios
|
||||||
|
.post("/sendemail", {
|
||||||
from: {
|
from: {
|
||||||
name: bodyshop.shopname,
|
name: bodyshop.shopname,
|
||||||
address: bodyshop.email,
|
address: bodyshop.email
|
||||||
},
|
},
|
||||||
ReplyTo: {
|
ReplyTo: {
|
||||||
Email: 'noreply@imex.online'
|
Email: "noreply@imex.online"
|
||||||
},
|
},
|
||||||
to: values.assigned_to,
|
to: values.assigned_to,
|
||||||
subject: `A Task has been re-assigned to you on ${bodyshop.shopname} - ${values.title}`,
|
subject: `A Task has been re-assigned to you on ${bodyshop.shopname} - ${values.title}`,
|
||||||
templateStrings: {
|
templateStrings: {
|
||||||
header: values.title,
|
header: values.title,
|
||||||
subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format('MM/DD/YYYY')}` : ''}`,
|
subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format("MM/DD/YYYY")}` : ""}`,
|
||||||
body: `<a href="${window.location.protocol}//${window.location.host}/manage/tasks/alltasks?taskid=${existingTask.id}">Please sign in to your account to view the task details.</a>`
|
body: `<a href="${window.location.protocol}//${window.location.host}/manage/tasks/alltasks?taskid=${existingTask.id}">Please sign in to your account to view the task details.</a>`
|
||||||
}
|
}
|
||||||
}).catch(e => console.error(`Something went wrong sending email to Assigned party on Task creation. ${e.message || ''}`));
|
})
|
||||||
|
.catch((e) =>
|
||||||
|
console.error(`Something went wrong sending email to Assigned party on Task creation. ${e.message || ""}`)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.dispatchEvent(new CustomEvent('taskUpdated', {
|
window.dispatchEvent(
|
||||||
detail: {message: 'A task has been created or edited.'},
|
new CustomEvent("taskUpdated", {
|
||||||
}));
|
detail: { message: "A task has been created or edited." }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("tasks.successes.updated"),
|
message: t("tasks.successes.updated")
|
||||||
});
|
});
|
||||||
|
|
||||||
if (refetch) await refetch();
|
if (refetch) await refetch();
|
||||||
@@ -189,9 +177,9 @@ export function TaskUpsertModalContainer({
|
|||||||
...values,
|
...values,
|
||||||
created_by: currentUser.email,
|
created_by: currentUser.email,
|
||||||
bodyshopid: bodyshop.id
|
bodyshopid: bodyshop.id
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const newTask = newTaskData?.data?.insert_tasks?.returning[0];
|
const newTask = newTaskData?.data?.insert_tasks?.returning[0];
|
||||||
@@ -200,10 +188,7 @@ export function TaskUpsertModalContainer({
|
|||||||
if (!newTaskData.errors) {
|
if (!newTaskData.errors) {
|
||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: newTask.jobid,
|
jobid: newTask.jobid,
|
||||||
operation: AuditTrailMapping.tasksCreated(
|
operation: AuditTrailMapping.tasksCreated(newTask.title, currentUser.email),
|
||||||
newTask.title,
|
|
||||||
currentUser.email
|
|
||||||
),
|
|
||||||
type: "tasksCreated"
|
type: "tasksCreated"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -215,30 +200,36 @@ export function TaskUpsertModalContainer({
|
|||||||
|
|
||||||
// send notification to the assigned user
|
// send notification to the assigned user
|
||||||
// TODO: This is being moved serverside
|
// TODO: This is being moved serverside
|
||||||
axios.post("/sendemail", {
|
axios
|
||||||
|
.post("/sendemail", {
|
||||||
from: {
|
from: {
|
||||||
name: bodyshop.shopname,
|
name: bodyshop.shopname,
|
||||||
address: bodyshop.email,
|
address: bodyshop.email
|
||||||
},
|
},
|
||||||
replyTo: {
|
replyTo: {
|
||||||
Email: 'noreply@imex.online'
|
Email: "noreply@imex.online"
|
||||||
},
|
},
|
||||||
to: values.assigned_to,
|
to: values.assigned_to,
|
||||||
subject: `A new Task has been assigned to you on ${bodyshop.shopname} - ${values.title}`,
|
subject: `A new Task has been assigned to you on ${bodyshop.shopname} - ${values.title}`,
|
||||||
templateName: 'taskAssigned',
|
templateName: "taskAssigned",
|
||||||
templateStrings: {
|
templateStrings: {
|
||||||
header: values.title,
|
header: values.title,
|
||||||
subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format('MM/DD/YYYY')}` : ''}`,
|
subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format("MM/DD/YYYY")}` : ""}`,
|
||||||
body: `<a href="${window.location.protocol}//${window.location.host}/manage/tasks/alltasks?taskid=${newTaskID}">Please sign to your account to view the task details.</a>`
|
body: `<a href="${window.location.protocol}//${window.location.host}/manage/tasks/alltasks?taskid=${newTaskID}">Please sign to your account to view the task details.</a>`
|
||||||
}
|
}
|
||||||
}).catch(e => console.error(`Something went wrong sending email to Assigned party on Task edit. ${e.message || ''}`));
|
})
|
||||||
|
.catch((e) =>
|
||||||
|
console.error(`Something went wrong sending email to Assigned party on Task edit. ${e.message || ""}`)
|
||||||
|
);
|
||||||
|
|
||||||
window.dispatchEvent(new CustomEvent('taskUpdated', {
|
window.dispatchEvent(
|
||||||
detail: {message: 'A task has been created or edited.'},
|
new CustomEvent("taskUpdated", {
|
||||||
}));
|
detail: { message: "A task has been created or edited." }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("tasks.successes.created"),
|
message: t("tasks.successes.created")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -273,18 +264,18 @@ export function TaskUpsertModalContainer({
|
|||||||
destroyOnClose
|
destroyOnClose
|
||||||
>
|
>
|
||||||
<Form form={form} onFinish={handleFinish} layout="vertical">
|
<Form form={form} onFinish={handleFinish} layout="vertical">
|
||||||
<TaskUpsertModalComponent form={form} loading={loading || (taskId && taskLoading)}
|
<TaskUpsertModalComponent
|
||||||
error={error} data={data}
|
form={form}
|
||||||
|
loading={loading || (taskId && taskLoading)}
|
||||||
|
error={error}
|
||||||
|
data={data}
|
||||||
selectedJobId={selectedJobId}
|
selectedJobId={selectedJobId}
|
||||||
setSelectedJobId={setSelectedJobId}
|
setSelectedJobId={setSelectedJobId}
|
||||||
selectedJobDetails={selectedJobDetails}/>
|
selectedJobDetails={selectedJobDetails}
|
||||||
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(mapStateToProps, mapDispatchToProps)(TaskUpsertModalContainer);
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(TaskUpsertModalContainer);
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { gql } from "@apollo/client";
|
import { gql } from "@apollo/client";
|
||||||
|
|
||||||
|
|
||||||
const PARTIAL_TASK_FIELDS = gql`
|
const PARTIAL_TASK_FIELDS = gql`
|
||||||
fragment TaskFields on tasks {
|
fragment TaskFields on tasks {
|
||||||
id
|
id
|
||||||
@@ -70,7 +69,8 @@ export const QUERY_GET_TASK_BY_ID = gql`
|
|||||||
tasks_by_pk(id: $id) {
|
tasks_by_pk(id: $id) {
|
||||||
...TaskFields
|
...TaskFields
|
||||||
}
|
}
|
||||||
}`;
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const QUERY_ALL_TASKS_PAGINATED = gql`
|
export const QUERY_ALL_TASKS_PAGINATED = gql`
|
||||||
${PARTIAL_TASK_FIELDS}
|
${PARTIAL_TASK_FIELDS}
|
||||||
@@ -88,9 +88,9 @@ export const QUERY_ALL_TASKS_PAGINATED = gql`
|
|||||||
limit: $limit
|
limit: $limit
|
||||||
order_by: $order
|
order_by: $order
|
||||||
where: {
|
where: {
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -98,9 +98,9 @@ export const QUERY_ALL_TASKS_PAGINATED = gql`
|
|||||||
}
|
}
|
||||||
tasks_aggregate(
|
tasks_aggregate(
|
||||||
where: {
|
where: {
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -129,10 +129,10 @@ export const QUERY_JOBLINE_TASKS_PAGINATED = gql`
|
|||||||
limit: $limit
|
limit: $limit
|
||||||
order_by: $order
|
order_by: $order
|
||||||
where: {
|
where: {
|
||||||
joblineid: {_eq: $joblineid},
|
joblineid: { _eq: $joblineid }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -140,10 +140,10 @@ export const QUERY_JOBLINE_TASKS_PAGINATED = gql`
|
|||||||
}
|
}
|
||||||
tasks_aggregate(
|
tasks_aggregate(
|
||||||
where: {
|
where: {
|
||||||
joblineid: {_eq: $joblineid},
|
joblineid: { _eq: $joblineid }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -172,10 +172,10 @@ export const QUERY_PARTSORDER_TASKS_PAGINATED = gql`
|
|||||||
limit: $limit
|
limit: $limit
|
||||||
order_by: $order
|
order_by: $order
|
||||||
where: {
|
where: {
|
||||||
partsorderid: {_eq: $partsorderid},
|
partsorderid: { _eq: $partsorderid }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -183,10 +183,10 @@ export const QUERY_PARTSORDER_TASKS_PAGINATED = gql`
|
|||||||
}
|
}
|
||||||
tasks_aggregate(
|
tasks_aggregate(
|
||||||
where: {
|
where: {
|
||||||
partsorderid: {_eq: $partsorderid},
|
partsorderid: { _eq: $partsorderid }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -215,10 +215,10 @@ export const QUERY_BILL_TASKS_PAGINATED = gql`
|
|||||||
limit: $limit
|
limit: $limit
|
||||||
order_by: $order
|
order_by: $order
|
||||||
where: {
|
where: {
|
||||||
billid: {_eq: $billid},
|
billid: { _eq: $billid }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -226,10 +226,10 @@ export const QUERY_BILL_TASKS_PAGINATED = gql`
|
|||||||
}
|
}
|
||||||
tasks_aggregate(
|
tasks_aggregate(
|
||||||
where: {
|
where: {
|
||||||
billid: {_eq: $billid},
|
billid: { _eq: $billid }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -258,10 +258,10 @@ export const QUERY_JOB_TASKS_PAGINATED = gql`
|
|||||||
limit: $limit
|
limit: $limit
|
||||||
order_by: $order
|
order_by: $order
|
||||||
where: {
|
where: {
|
||||||
jobid: {_eq: $jobid},
|
jobid: { _eq: $jobid }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -269,10 +269,10 @@ export const QUERY_JOB_TASKS_PAGINATED = gql`
|
|||||||
}
|
}
|
||||||
tasks_aggregate(
|
tasks_aggregate(
|
||||||
where: {
|
where: {
|
||||||
jobid: {_eq: $jobid},
|
jobid: { _eq: $jobid }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
assigned_to: {_eq: $assigned_to},
|
assigned_to: { _eq: $assigned_to }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -299,9 +299,9 @@ export const QUERY_MY_TASKS_PAGINATED = gql`
|
|||||||
limit: $limit
|
limit: $limit
|
||||||
order_by: $order
|
order_by: $order
|
||||||
where: {
|
where: {
|
||||||
assigned_to: {_eq: $user},
|
assigned_to: { _eq: $user }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -309,9 +309,9 @@ export const QUERY_MY_TASKS_PAGINATED = gql`
|
|||||||
}
|
}
|
||||||
tasks_aggregate(
|
tasks_aggregate(
|
||||||
where: {
|
where: {
|
||||||
assigned_to: {_eq: $user},
|
assigned_to: { _eq: $user }
|
||||||
bodyshopid: {_eq: $bodyshop},
|
bodyshopid: { _eq: $bodyshop }
|
||||||
deleted: {_eq: $deleted},
|
deleted: { _eq: $deleted }
|
||||||
completed: { _eq: $completed }
|
completed: { _eq: $completed }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -414,4 +414,4 @@ export const MUTATION_UPDATE_TASK = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`;
|
||||||
|
|||||||
@@ -20,38 +20,28 @@ import {FaHardHat, FaRegStickyNote, FaShieldAlt, FaTasks} from "react-icons/fa";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import FormFieldsChanged
|
import FormFieldsChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
|
||||||
from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
|
|
||||||
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
|
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
|
||||||
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
|
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
|
||||||
import JobLifecycleComponent from "../../components/job-lifecycle/job-lifecycle.component";
|
import JobLifecycleComponent from "../../components/job-lifecycle/job-lifecycle.component";
|
||||||
import JobLineUpsertModalContainer
|
import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
|
||||||
from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
|
import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
|
||||||
import JobReconciliationModal
|
|
||||||
from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
|
|
||||||
import JobSyncButton from "../../components/job-sync-button/job-sync-button.component";
|
import JobSyncButton from "../../components/job-sync-button/job-sync-button.component";
|
||||||
import JobsChangeStatus from "../../components/jobs-change-status/jobs-change-status.component";
|
import JobsChangeStatus from "../../components/jobs-change-status/jobs-change-status.component";
|
||||||
import JobsConvertButton from "../../components/jobs-convert-button/jobs-convert-button.component";
|
import JobsConvertButton from "../../components/jobs-convert-button/jobs-convert-button.component";
|
||||||
import JobsDetailDatesComponent
|
import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
|
||||||
from "../../components/jobs-detail-dates/jobs-detail-dates.component";
|
|
||||||
import JobsDetailGeneral from "../../components/jobs-detail-general/jobs-detail-general.component";
|
import JobsDetailGeneral from "../../components/jobs-detail-general/jobs-detail-general.component";
|
||||||
import JobsDetailHeaderActions
|
import JobsDetailHeaderActions from "../../components/jobs-detail-header-actions/jobs-detail-header-actions.component";
|
||||||
from "../../components/jobs-detail-header-actions/jobs-detail-header-actions.component";
|
|
||||||
import JobsDetailHeader from "../../components/jobs-detail-header/jobs-detail-header.component";
|
import JobsDetailHeader from "../../components/jobs-detail-header/jobs-detail-header.component";
|
||||||
import JobsDetailLaborContainer
|
import JobsDetailLaborContainer from "../../components/jobs-detail-labor/jobs-detail-labor.container";
|
||||||
from "../../components/jobs-detail-labor/jobs-detail-labor.container";
|
|
||||||
import JobsDetailPliContainer from "../../components/jobs-detail-pli/jobs-detail-pli.container";
|
import JobsDetailPliContainer from "../../components/jobs-detail-pli/jobs-detail-pli.container";
|
||||||
import JobsDetailRates from "../../components/jobs-detail-rates/jobs-detail-rates.component";
|
import JobsDetailRates from "../../components/jobs-detail-rates/jobs-detail-rates.component";
|
||||||
import JobsDetailTotals from "../../components/jobs-detail-totals/jobs-detail-totals.component";
|
import JobsDetailTotals from "../../components/jobs-detail-totals/jobs-detail-totals.component";
|
||||||
import JobsDocumentsGalleryContainer
|
import JobsDocumentsGalleryContainer from "../../components/jobs-documents-gallery/jobs-documents-gallery.container";
|
||||||
from "../../components/jobs-documents-gallery/jobs-documents-gallery.container";
|
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||||
import JobsDocumentsLocalGallery
|
|
||||||
from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
|
||||||
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
|
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
|
||||||
import NoteUpsertModalComponent
|
import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container";
|
||||||
from "../../components/note-upsert-modal/note-upsert-modal.container";
|
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
|
||||||
import ScheduleJobModalContainer
|
|
||||||
from "../../components/schedule-job-modal/schedule-job-modal.container";
|
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
@@ -59,8 +49,7 @@ import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors
|
|||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import UndefinedToNull from "../../utils/undefinedtonull";
|
import UndefinedToNull from "../../utils/undefinedtonull";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import JobProfileDataWarning
|
import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component";
|
||||||
from "../../components/job-profile-data-warning/job-profile-data-warning.component";
|
|
||||||
import { DateTimeFormat } from "../../utils/DateFormatter";
|
import { DateTimeFormat } from "../../utils/DateFormatter";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
|
||||||
@@ -73,15 +62,21 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
currentUser: selectCurrentUser
|
currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPrintCenterContext: (context) => dispatch(setModalContext({
|
setPrintCenterContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
context: context,
|
context: context,
|
||||||
modal: "printCenter"
|
modal: "printCenter"
|
||||||
})),
|
})
|
||||||
insertAuditTrail: ({jobid, operation, type}) => dispatch(insertAuditTrail({
|
),
|
||||||
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
|
dispatch(
|
||||||
|
insertAuditTrail({
|
||||||
jobid,
|
jobid,
|
||||||
operation,
|
operation,
|
||||||
type
|
type
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobsDetailPage({
|
export function JobsDetailPage({
|
||||||
@@ -111,13 +106,13 @@ export function JobsDetailPage({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleTaskUpdated = async (event) => {
|
const handleTaskUpdated = async (event) => {
|
||||||
await refetch().catch(e => `Something went wrong fetching tasks: ${e.message || ''}`);
|
await refetch().catch((e) => `Something went wrong fetching tasks: ${e.message || ""}`);
|
||||||
};
|
};
|
||||||
window.addEventListener('taskUpdated', handleTaskUpdated);
|
window.addEventListener("taskUpdated", handleTaskUpdated);
|
||||||
|
|
||||||
// Clean up the event listener when the component is unmounted.
|
// Clean up the event listener when the component is unmounted.
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('taskUpdated', handleTaskUpdated);
|
window.removeEventListener("taskUpdated", handleTaskUpdated);
|
||||||
};
|
};
|
||||||
}, [refetch]);
|
}, [refetch]);
|
||||||
|
|
||||||
@@ -272,8 +267,7 @@ export function JobsDetailPage({
|
|||||||
<PrinterFilled />
|
<PrinterFilled />
|
||||||
{t("jobs.actions.printCenter")}
|
{t("jobs.actions.printCenter")}
|
||||||
</Button>
|
</Button>
|
||||||
<JobsConvertButton job={job} refetch={refetch}
|
<JobsConvertButton job={job} refetch={refetch} parentFormIsFieldsTouched={form.isFieldsTouched} />
|
||||||
parentFormIsFieldsTouched={form.isFieldsTouched}/>
|
|
||||||
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
||||||
<Button type="primary" loading={loading} disabled={jobRO} onClick={() => form.submit()}>
|
<Button type="primary" loading={loading} disabled={jobRO} onClick={() => form.submit()}>
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
@@ -321,8 +315,7 @@ export function JobsDetailPage({
|
|||||||
icon: <BarsOutlined />,
|
icon: <BarsOutlined />,
|
||||||
label: t("menus.jobsdetail.repairdata"),
|
label: t("menus.jobsdetail.repairdata"),
|
||||||
forceRender: true,
|
forceRender: true,
|
||||||
children: <JobsLinesContainer job={job} joblines={job.joblines} refetch={refetch}
|
children: <JobsLinesContainer job={job} joblines={job.joblines} refetch={refetch} form={form} />
|
||||||
form={form}/>
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "rates",
|
key: "rates",
|
||||||
@@ -403,17 +396,26 @@ export function JobsDetailPage({
|
|||||||
children: <JobAuditTrail jobId={job.id} />
|
children: <JobAuditTrail jobId={job.id} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'tasks',
|
key: "tasks",
|
||||||
icon: <FaTasks />,
|
icon: <FaTasks />,
|
||||||
label: <Space direction='horizontal'>
|
label: (
|
||||||
{t("jobs.labels.tasks")}{job.tasks_aggregate.aggregate.count > 0 &&
|
<Space direction="horizontal">
|
||||||
<Badge count={job.tasks_aggregate.aggregate.count}/>}
|
{t("jobs.labels.tasks")}
|
||||||
</Space>,
|
{job.tasks_aggregate.aggregate.count > 0 && <Badge count={job.tasks_aggregate.aggregate.count} />}
|
||||||
children: <TaskListContainer currentUser={currentUser} bodyshop={bodyshop}
|
</Space>
|
||||||
relationshipType={'jobid'} relationshipId={job.id}
|
),
|
||||||
|
children: (
|
||||||
|
<TaskListContainer
|
||||||
|
currentUser={currentUser}
|
||||||
|
bodyshop={bodyshop}
|
||||||
|
relationshipType={"jobid"}
|
||||||
|
relationshipId={job.id}
|
||||||
query={QUERY_JOB_TASKS_PAGINATED}
|
query={QUERY_JOB_TASKS_PAGINATED}
|
||||||
titleTranslation='tasks.titles.job_tasks' showRo={false}/>
|
titleTranslation="tasks.titles.job_tasks"
|
||||||
},
|
showRo={false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -17,26 +17,21 @@ import TestComponent from "../../components/_test/test.page";
|
|||||||
import HeaderContainer from "../../components/header/header.container";
|
import HeaderContainer from "../../components/header/header.container";
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import PartnerPingComponent from "../../components/partner-ping/partner-ping.component";
|
import PartnerPingComponent from "../../components/partner-ping/partner-ping.component";
|
||||||
import PrintCenterModalContainer
|
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
|
||||||
from "../../components/print-center-modal/print-center-modal.container";
|
|
||||||
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
||||||
import { requestForToken } from "../../firebase/firebase.utils";
|
import { requestForToken } from "../../firebase/firebase.utils";
|
||||||
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||||
import { setJoyRideFinished } from "../../redux/application/application.actions.js";
|
import { setJoyRideFinished } from "../../redux/application/application.actions.js";
|
||||||
import {
|
import { selectEnableJoyRide, selectJoyRideSteps } from "../../redux/application/application.selectors.js";
|
||||||
selectEnableJoyRide,
|
|
||||||
selectJoyRideSteps
|
|
||||||
} from "../../redux/application/application.selectors.js";
|
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
||||||
import "./manage.page.styles.scss";
|
import "./manage.page.styles.scss";
|
||||||
|
|
||||||
|
|
||||||
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
||||||
|
|
||||||
const CardPaymentModalContainer = lazy(() =>
|
const CardPaymentModalContainer = lazy(
|
||||||
import("../../components/card-payment-modal/card-payment-modal.container.")
|
() => import("../../components/card-payment-modal/card-payment-modal.container.")
|
||||||
);
|
);
|
||||||
|
|
||||||
const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page.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 ReportCenterModal = lazy(() => import("../../components/report-center-modal/report-center-modal.container"));
|
||||||
const BillEnterModalContainer = lazy(() => import("../../components/bill-enter-modal/bill-enter-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 TimeTicketModalContainer = lazy(() => import("../../components/time-ticket-modal/time-ticket-modal.container"));
|
||||||
const TimeTicketModalTask = lazy(() =>
|
const TimeTicketModalTask = lazy(
|
||||||
import("../../components/time-ticket-task-modal/time-ticket-task-modal.container")
|
() => import("../../components/time-ticket-task-modal/time-ticket-task-modal.container")
|
||||||
);
|
);
|
||||||
const PaymentModalContainer = lazy(() => import("../../components/payment-modal/payment-modal.container"));
|
const PaymentModalContainer = lazy(() => import("../../components/payment-modal/payment-modal.container"));
|
||||||
const ProductionListPage = lazy(() => import("../production-list/production-list.container"));
|
const ProductionListPage = lazy(() => import("../production-list/production-list.container"));
|
||||||
@@ -260,18 +255,20 @@ export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyR
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path='/tasks/mytasks'
|
path="/tasks/mytasks"
|
||||||
element={
|
element={
|
||||||
<Suspense fallback={<Spin />}>
|
<Suspense fallback={<Spin />}>
|
||||||
<MyTasksPage />
|
<MyTasksPage />
|
||||||
</Suspense>}
|
</Suspense>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path='/tasks/alltasks'
|
path="/tasks/alltasks"
|
||||||
element={
|
element={
|
||||||
<Suspense fallback={<Spin />}>
|
<Suspense fallback={<Spin />}>
|
||||||
<AllTasksPage />
|
<AllTasksPage />
|
||||||
</Suspense>}
|
</Suspense>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/inventory/"
|
path="/inventory/"
|
||||||
@@ -647,8 +644,7 @@ export function Manage({conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyR
|
|||||||
Get Tours
|
Get Tours
|
||||||
</Button>
|
</Button>
|
||||||
{tours.map((tour) => (
|
{tours.map((tour) => (
|
||||||
<Tag key={tour.id}
|
<Tag key={tour.id} onClick={() => window.productFruits.api.tours.tryStartTour(tour.id)}>
|
||||||
onClick={() => window.productFruits.api.tours.tryStartTour(tour.id)}>
|
|
||||||
{tour.name}
|
{tour.name}
|
||||||
</Tag>
|
</Tag>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ import {useLocation} from "react-router-dom";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||||
setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})),
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function MyTasksPageContainer({
|
export function MyTasksPageContainer({
|
||||||
@@ -31,45 +31,40 @@ export function MyTasksPageContainer({
|
|||||||
setTaskUpsertContext
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const searchParams = queryString.parse((useLocation().search));
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("titles.all_tasks", {
|
document.title = t("titles.all_tasks", {
|
||||||
app: InstanceRenderManager({
|
app: InstanceRenderManager({
|
||||||
imex: '$t(titles.imexonline)',
|
imex: "$t(titles.imexonline)",
|
||||||
rome: '$t(titles.romeonline)',
|
rome: "$t(titles.romeonline)",
|
||||||
promanager: '$t(titles.promanager)'
|
promanager: "$t(titles.promanager)"
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
setSelectedHeader("all_tasks");
|
setSelectedHeader("all_tasks");
|
||||||
setBreadcrumbs([
|
setBreadcrumbs([
|
||||||
{
|
{
|
||||||
link: "/manage/tasks/alltasks",
|
link: "/manage/tasks/alltasks",
|
||||||
label: t("titles.bc.all_tasks"),
|
label: t("titles.bc.all_tasks")
|
||||||
},]);
|
}
|
||||||
|
]);
|
||||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||||
|
|
||||||
// This takes care of the ability to deep link a task from the URL (Dispatches the modal)
|
// This takes care of the ability to deep link a task from the URL (Dispatches the modal)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Check for a query string in the URL
|
// Check for a query string in the URL
|
||||||
const urlParams = new URLSearchParams(searchParams);
|
const urlParams = new URLSearchParams(searchParams);
|
||||||
const taskId = urlParams.get('taskid');
|
const taskId = urlParams.get("taskid");
|
||||||
if (taskId) {
|
if (taskId) {
|
||||||
setTaskUpsertContext({
|
setTaskUpsertContext({
|
||||||
context: {
|
context: {
|
||||||
taskId,
|
taskId
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
urlParams.delete('taskid');
|
urlParams.delete("taskid");
|
||||||
}
|
}
|
||||||
}, [setTaskUpsertContext]);
|
}, [setTaskUpsertContext]);
|
||||||
|
|
||||||
return (
|
return <TasksPageComponent type={TaskPageTypes.ALL_TASKS} currentUser={currentUser} bodyshop={bodyshop} />;
|
||||||
<TasksPageComponent type={TaskPageTypes.ALL_TASKS} currentUser={currentUser}
|
|
||||||
bodyshop={bodyshop}/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(mapStateToProps, mapDispatchToProps)(MyTasksPageContainer);
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(MyTasksPageContainer);
|
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import TaskPageTypes from "./taskPageTypes.jsx";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function MyTasksPageContainer({ bodyshop, currentUser, setBreadcrumbs, setSelectedHeader }) {
|
export function MyTasksPageContainer({ bodyshop, currentUser, setBreadcrumbs, setSelectedHeader }) {
|
||||||
@@ -24,26 +24,21 @@ export function MyTasksPageContainer({bodyshop, currentUser, setBreadcrumbs, set
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("titles.my_tasks", {
|
document.title = t("titles.my_tasks", {
|
||||||
app: InstanceRenderManager({
|
app: InstanceRenderManager({
|
||||||
imex: '$t(titles.imexonline)',
|
imex: "$t(titles.imexonline)",
|
||||||
rome: '$t(titles.romeonline)',
|
rome: "$t(titles.romeonline)",
|
||||||
promanager: '$t(titles.promanager)'
|
promanager: "$t(titles.promanager)"
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
setSelectedHeader("my_tasks");
|
setSelectedHeader("my_tasks");
|
||||||
setBreadcrumbs([
|
setBreadcrumbs([
|
||||||
{
|
{
|
||||||
link: "/manage/tasks/mytasks",
|
link: "/manage/tasks/mytasks",
|
||||||
label: t("titles.bc.my_tasks"),
|
label: t("titles.bc.my_tasks")
|
||||||
},]);
|
}
|
||||||
|
]);
|
||||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||||
|
|
||||||
return (
|
return <TasksPageComponent type={TaskPageTypes.MY_TASKS} currentUser={currentUser} bodyshop={bodyshop} />;
|
||||||
<TasksPageComponent type={TaskPageTypes.MY_TASKS} currentUser={currentUser}
|
|
||||||
bodyshop={bodyshop}/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(mapStateToProps, mapDispatchToProps)(MyTasksPageContainer);
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(MyTasksPageContainer);
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export const TaskPageTypes = {
|
export const TaskPageTypes = {
|
||||||
MY_TASKS: 'myTasks',
|
MY_TASKS: "myTasks",
|
||||||
ALL_TASKS: 'allTasks',
|
ALL_TASKS: "allTasks"
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TaskPageTypes;
|
export default TaskPageTypes;
|
||||||
|
|||||||
@@ -6,16 +6,27 @@ import taskPageTypes from "./taskPageTypes.jsx";
|
|||||||
export default function TasksPageComponent({ bodyshop, currentUser, type }) {
|
export default function TasksPageComponent({ bodyshop, currentUser, type }) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case taskPageTypes.MY_TASKS:
|
case taskPageTypes.MY_TASKS:
|
||||||
return <TaskListContainer onlyMine={true} relationshipId={currentUser.email}
|
return (
|
||||||
relationshipType={'user'} query={QUERY_MY_TASKS_PAGINATED}
|
<TaskListContainer
|
||||||
bodyshop={bodyshop} titleTranslation={'tasks.titles.my_tasks'}
|
onlyMine={true}
|
||||||
currentUser={currentUser}/>
|
relationshipId={currentUser.email}
|
||||||
|
relationshipType={"user"}
|
||||||
|
query={QUERY_MY_TASKS_PAGINATED}
|
||||||
|
bodyshop={bodyshop}
|
||||||
|
titleTranslation={"tasks.titles.my_tasks"}
|
||||||
|
currentUser={currentUser}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case taskPageTypes.ALL_TASKS:
|
case taskPageTypes.ALL_TASKS:
|
||||||
return <TaskListContainer query={QUERY_ALL_TASKS_PAGINATED} bodyshop={bodyshop}
|
return (
|
||||||
titleTranslation={'tasks.titles.all_tasks'}
|
<TaskListContainer
|
||||||
currentUser={currentUser}/>
|
query={QUERY_ALL_TASKS_PAGINATED}
|
||||||
|
bodyshop={bodyshop}
|
||||||
|
titleTranslation={"tasks.titles.all_tasks"}
|
||||||
|
currentUser={currentUser}
|
||||||
|
/>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return <></>
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const INITIAL_STATE = {
|
|||||||
billEnter: { ...baseModal },
|
billEnter: { ...baseModal },
|
||||||
courtesyCarReturn: { ...baseModal },
|
courtesyCarReturn: { ...baseModal },
|
||||||
noteUpsert: { ...baseModal },
|
noteUpsert: { ...baseModal },
|
||||||
taskUpsert: {...baseModal, },
|
taskUpsert: { ...baseModal },
|
||||||
schedule: { ...baseModal },
|
schedule: { ...baseModal },
|
||||||
partsOrder: { ...baseModal },
|
partsOrder: { ...baseModal },
|
||||||
timeTicket: { ...baseModal },
|
timeTicket: { ...baseModal },
|
||||||
|
|||||||
@@ -41,32 +41,36 @@ const AuditTrailMapping = {
|
|||||||
jobsuspend: (status) => i18n.t("audit_trail.messages.jobsuspend", { status }),
|
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
|
// Tasks Entries
|
||||||
tasksCreated: (title, createdBy) => i18n.t("audit_trail.messages.tasks_created", {
|
tasksCreated: (title, createdBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_created", {
|
||||||
title,
|
title,
|
||||||
createdBy
|
createdBy
|
||||||
}),
|
}),
|
||||||
tasksUpdated: (title, updatedBy) => i18n.t("audit_trail.messages.tasks_updated", {
|
tasksUpdated: (title, updatedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_updated", {
|
||||||
title,
|
title,
|
||||||
updatedBy
|
updatedBy
|
||||||
}),
|
}),
|
||||||
tasksDeleted: (title, deletedBy) => i18n.t("audit_trail.messages.tasks_deleted", {
|
tasksDeleted: (title, deletedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_deleted", {
|
||||||
title,
|
title,
|
||||||
deletedBy
|
deletedBy
|
||||||
}),
|
}),
|
||||||
tasksUndeleted: (title, undeletedBy) => i18n.t("audit_trail.messages.tasks_undeleted", {
|
tasksUndeleted: (title, undeletedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_undeleted", {
|
||||||
title,
|
title,
|
||||||
undeletedBy
|
undeletedBy
|
||||||
}),
|
}),
|
||||||
tasksCompleted: (title, completedBy) => i18n.t("audit_trail.messages.tasks_completed", {
|
tasksCompleted: (title, completedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_completed", {
|
||||||
title,
|
title,
|
||||||
completedBy
|
completedBy
|
||||||
}),
|
}),
|
||||||
tasksUncompleted: (title, uncompletedBy) => i18n.t("audit_trail.messages.tasks_uncompleted", {
|
tasksUncompleted: (title, uncompletedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_uncompleted", {
|
||||||
title,
|
title,
|
||||||
uncompletedBy
|
uncompletedBy
|
||||||
}),
|
})
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AuditTrailMapping;
|
export default AuditTrailMapping;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function replaceUndefinedWithNull(obj, keys) {
|
|||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(obj).map(([key, value]) => {
|
Object.entries(obj).map(([key, value]) => {
|
||||||
if (keys) {
|
if (keys) {
|
||||||
return [key, (keys.includes(key) && value === undefined) ? null : value];
|
return [key, keys.includes(key) && value === undefined ? null : value];
|
||||||
} else {
|
} else {
|
||||||
return [key, value === undefined ? null : value];
|
return [key, value === undefined ? null : value];
|
||||||
}
|
}
|
||||||
|
|||||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -56,6 +56,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
|
"prettier": "^3.2.5",
|
||||||
"source-map-explorer": "^2.5.2"
|
"source-map-explorer": "^2.5.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -5900,6 +5901,21 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
|
||||||
|
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/process": {
|
"node_modules/process": {
|
||||||
"version": "0.11.10",
|
"version": "0.11.10",
|
||||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
|
"prettier": "^3.2.5",
|
||||||
"source-map-explorer": "^2.5.2"
|
"source-map-explorer": "^2.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,21 +13,21 @@ const {header, end, start} = require("./html");
|
|||||||
|
|
||||||
const defaultFooter = () => {
|
const defaultFooter = () => {
|
||||||
return RenderInstanceManager({
|
return RenderInstanceManager({
|
||||||
imex: 'ImEX Online Collision Repair Management System',
|
imex: "ImEX Online Collision Repair Management System",
|
||||||
rome: 'Rome Technologies',
|
rome: "Rome Technologies",
|
||||||
promanager: 'ProManager',
|
promanager: "ProManager"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const now = () => moment().format('MM/DD/YYYY @ hh:mm a');
|
const now = () => moment().format("MM/DD/YYYY @ hh:mm a");
|
||||||
|
|
||||||
const generateEmailTemplate = (strings) => {
|
const generateEmailTemplate = (strings) => {
|
||||||
|
return (
|
||||||
return `
|
`
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">`
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">` +
|
||||||
+ header
|
header +
|
||||||
+ start
|
start +
|
||||||
+ `
|
`
|
||||||
<table class="row">
|
<table class="row">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -101,8 +101,9 @@ const generateEmailTemplate = (strings) => {
|
|||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>`
|
</table>` +
|
||||||
+ end
|
end
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = generateEmailTemplate;
|
module.exports = generateEmailTemplate;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const logger = require("../utils/logger");
|
|||||||
const client = require("../graphql-client/graphql-client").client;
|
const client = require("../graphql-client/graphql-client").client;
|
||||||
const queries = require("../graphql-client/queries");
|
const queries = require("../graphql-client/queries");
|
||||||
const { isObject } = require("lodash");
|
const { isObject } = require("lodash");
|
||||||
const generateEmailTemplate = require('./generateTemplate');
|
const generateEmailTemplate = require("./generateTemplate");
|
||||||
|
|
||||||
const ses = new aws.SES({
|
const ses = new aws.SES({
|
||||||
// The key apiVersion is no longer supported in v3, and can be removed.
|
// The key apiVersion is no longer supported in v3, and can be removed.
|
||||||
|
|||||||
Reference in New Issue
Block a user