- Progress Commit

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-04-01 14:45:55 -04:00
parent 54dc9c8587
commit ae07f71e76
17 changed files with 223 additions and 78 deletions

View File

@@ -1,29 +1,35 @@
import { EditFilled, SyncOutlined } from "@ant-design/icons"; import {EditFilled, SyncOutlined} from "@ant-design/icons";
import { Button, Card, Checkbox, Input, Space, Table } from "antd"; import {Button, Card, Checkbox, Input, Space, Table} from "antd";
import React, { useState } from "react"; import React, {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 { 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";
import { selectBodyshop } from "../../redux/user/user.selectors"; import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter"; import {DateFormatter} from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters"; import {alphaSort, dateSort} from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants"; import {TemplateList} from "../../utils/TemplateConstants";
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component"; import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component"; import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component"; import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
import {FaTasks} from "react-icons/fa";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
bodyshop: selectBodyshop bodyshop: selectBodyshop,
currentUser: selectCurrentUser
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
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" })),
setReconciliationContext: (context) => dispatch(setModalContext({ context: context, modal: "reconciliation" })) setReconciliationContext: (context) => dispatch(setModalContext({
context: context,
modal: "reconciliation"
})),
setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})),
}); });
export function BillsListTableComponent({ export function BillsListTableComponent({
@@ -34,7 +40,9 @@ export function BillsListTableComponent({
handleOnRowClick, handleOnRowClick,
setPartsOrderContext, setPartsOrderContext,
setBillEnterContext, setBillEnterContext,
setReconciliationContext setReconciliationContext,
setTaskUpsertContext,
currentUser,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -48,6 +56,8 @@ export function BillsListTableComponent({
const Templates = TemplateList("bill"); const Templates = TemplateList("bill");
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 && (
@@ -55,6 +65,16 @@ export function BillsListTableComponent({
<EditFilled /> <EditFilled />
</Button> </Button>
)} )}
<Button title={t('tasks.buttons.create')} onClick={() => {
setTaskUpsertContext({
context: {
jobid: job.id,
billid: record.id
}
});
}}>
<FaTasks/>
</Button>
<BillDeleteButton bill={record} jobid={job.id} /> <BillDeleteButton bill={record} jobid={job.id} />
<BillDetailEditReturnComponent <BillDetailEditReturnComponent
data={{ bills_by_pk: { ...record, jobid: job.id } }} data={{ bills_by_pk: { ...record, jobid: job.id } }}

View File

@@ -10,17 +10,23 @@ import AlertComponent from "../alert/alert.component";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors";
import {
QUERY_JOB_TASKS_PAGINATED,
QUERY_JOBLINE_TASKS_PAGINATED
} from "../../graphql/tasks.queries.js";
import TaskListContainer from "../task-list/task-list.container.jsx";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop bodyshop: selectBodyshop,
currentUser: selectCurrentUser
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language)) //setUserLanguage: language => dispatch(setUserLanguage(language))
}); });
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpander); export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpander);
export function JobLinesExpander({ jobline, jobid, bodyshop }) { export function JobLinesExpander({ jobline, jobid, bodyshop, currentUser }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, { const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
@@ -128,6 +134,9 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
} }
/> />
</Col> </Col>
<Col md={24} lg={24}>
<TaskListContainer currentUser={currentUser} bodyshop={bodyshop} parentJobId={jobid} relationshipType={'joblineid'} relationshipId={jobline.id} query={QUERY_JOBLINE_TASKS_PAGINATED} titleTranslation='tasks.titles.job_tasks'/>
</Col>
</Row> </Row>
); );
} }

View File

@@ -42,6 +42,7 @@ import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container"; import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import JobLinesExpander from "./job-lines-expander.component"; import JobLinesExpander from "./job-lines-expander.component";
import JobLinesPartPriceChange from "./job-lines-part-price-change.component"; import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
import {FaTasks} from "react-icons/fa";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -52,7 +53,8 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({ 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'})),
}); });
export function JobLinesComponent({ export function JobLinesComponent({
@@ -67,7 +69,8 @@ export function JobLinesComponent({
job, job,
setJobLineEditContext, setJobLineEditContext,
form, form,
setBillEnterContext setBillEnterContext,
setTaskUpsertContext
}) { }) {
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK); const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
const { const {
@@ -331,6 +334,16 @@ export function JobLinesComponent({
> >
<EditFilled /> <EditFilled />
</Button> </Button>
<Button title={t('tasks.buttons.create')} onClick={() => {
setTaskUpsertContext({
context: {
jobid: job.id,
joblineid: record.id
}
});
}}>
<FaTasks/>
</Button>
<Button <Button
disabled={jobRO} disabled={jobRO}
onClick={async () => { onClick={async () => {

View File

@@ -96,7 +96,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
}), }),
onFilter: (value, record) => value.includes(record.status) onFilter: (value, record) => value.includes(record.status)
}, },
{ {
title: t("jobs.fields.vehicle"), title: t("jobs.fields.vehicle"),
dataIndex: "vehicle", dataIndex: "vehicle",

View File

@@ -176,7 +176,11 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
[], [],
onFilter: (value, record) => value.includes(record.status) onFilter: (value, record) => value.includes(record.status)
}, },
{
title: t('jobs.fields.active_tasks'),
key: 'activetasks',
render: (text, record) => <span>{record.tasks_aggregate.aggregate.count}</span>,
},
{ {
title: t("jobs.fields.vehicle"), title: t("jobs.fields.vehicle"),
dataIndex: "vehicle", dataIndex: "vehicle",

View File

@@ -1,50 +1,60 @@
import { DeleteFilled, EyeFilled, SyncOutlined } from "@ant-design/icons"; import {DeleteFilled, EyeFilled, SyncOutlined} from "@ant-design/icons";
import { useMutation } from "@apollo/client"; import {useMutation} from "@apollo/client";
import { Button, Card, Checkbox, Drawer, Grid, Input, Popconfirm, Space, Table } from "antd"; import {Button, Card, Checkbox, Drawer, Grid, Input, Popconfirm, Space, Table} from "antd";
import { PageHeader } from "@ant-design/pro-layout"; import {PageHeader} from "@ant-design/pro-layout";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, {useState} from "react";
import { useTranslation } from "react-i18next"; import {useTranslation} from "react-i18next";
import { connect } from "react-redux"; import {connect} from "react-redux";
import { useLocation } from "react-router-dom"; import {useLocation} from "react-router-dom";
import { createStructuredSelector } from "reselect"; import {createStructuredSelector} from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; import {logImEXEvent} from "../../firebase/firebase.utils";
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries"; import {DELETE_PARTS_ORDER} from "../../graphql/parts-orders.queries";
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";
import { selectBodyshop } from "../../redux/user/user.selectors"; import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter"; 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 from "../parts-order-backorder-eta/parts-order-backorder-eta.component"; import PartsOrderBackorderEta
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 from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component"; import PartsOrderLineBackorderButton
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";
import {FaTasks} from "react-icons/fa";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
bodyshop: selectBodyshop bodyshop: selectBodyshop,
currentUser: selectCurrentUser
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })), setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
setPartsReceiveContext: (context) => dispatch(setModalContext({ context: context, modal: "partsReceive" })) setPartsReceiveContext: (context) => dispatch(setModalContext({
context: context,
modal: "partsReceive"
})),
setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})),
}); });
export function PartsOrderListTableComponent({ export function PartsOrderListTableComponent({
setBillEnterContext, setBillEnterContext,
bodyshop, bodyshop,
jobRO, jobRO,
job, job,
billsQuery, billsQuery,
handleOnRowClick, handleOnRowClick,
setPartsReceiveContext setPartsReceiveContext,
setTaskUpsertContext,
currentUser
}) { }) {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1]) .filter((screen) => !!screen[1])
@@ -76,7 +86,7 @@ export function PartsOrderListTableComponent({
const { refetch } = billsQuery; const { refetch } = billsQuery;
const recordActions = (record, showView = false) => ( const recordActions = (record, showView = false) => (
<Space wrap> <Space direction='horizontal' wrap>
{showView && ( {showView && (
<Button onClick={() => handleOnRowClick(record)}> <Button onClick={() => handleOnRowClick(record)}>
<EyeFilled /> <EyeFilled />
@@ -108,7 +118,16 @@ export function PartsOrderListTableComponent({
> >
{t("parts_orders.actions.receive")} {t("parts_orders.actions.receive")}
</Button> </Button>
<Button title={t('tasks.buttons.create')} onClick={() => {
setTaskUpsertContext({
context: {
jobid: job.id,
partsorderid: record.id
}
});
}}>
<FaTasks/>
</Button>
<Popconfirm <Popconfirm
title={t("parts_orders.labels.confirmdelete")} title={t("parts_orders.labels.confirmdelete")}
disabled={jobRO} disabled={jobRO}

View File

@@ -90,7 +90,9 @@ function TaskListComponent({
setTaskUpsertContext, setTaskUpsertContext,
toggleDeletedStatus, toggleDeletedStatus,
relationshipType, relationshipType,
relationshipId relationshipId,
onlyMine ,
parentJobId
}) { }) {
const {t} = useTranslation(); const {t} = useTranslation();
@@ -102,12 +104,25 @@ function TaskListComponent({
sortcolumn, sortcolumn,
sortorder, sortorder,
deleted, deleted,
completed completed,
mine
} = search; } = search;
const history = useNavigate(); const history = useNavigate();
const columns = [];
const columns = [ if (!onlyMine) {
columns.push(
{
title: t("tasks.fields.assigned_to"),
dataIndex: "assigned_to",
key: "assigned_to",
width: '10%',
}
);
}
columns.push(
{ {
title: t("tasks.fields.job.ro_number"), title: t("tasks.fields.job.ro_number"),
dataIndex: ["job", "ro_number"], dataIndex: ["job", "ro_number"],
@@ -178,8 +193,8 @@ function TaskListComponent({
</Button> </Button>
</Space> </Space>
), ),
}, }
]; );
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
@@ -190,6 +205,7 @@ function TaskListComponent({
setTaskUpsertContext({ setTaskUpsertContext({
actions: {}, actions: {},
context: { context: {
jobid: parentJobId,
[relationshipType]: relationshipId, [relationshipType]: relationshipId,
}, },
}); });
@@ -219,6 +235,15 @@ function TaskListComponent({
const tasksExtra = useCallback(() => { const tasksExtra = useCallback(() => {
return ( return (
<Space direction='horizontal'> <Space direction='horizontal'>
{!onlyMine && (
<Switch
checkedChildren={t('tasks.buttons.myTasks')}
unCheckedChildren={t('tasks.buttons.allTasks')}
title={t('tasks.titles.mine')}
checked={mine === "true"}
onChange={(value) => handleSwitchChange('mine', value)}
/>
)}
<Switch <Switch
checkedChildren={<CheckCircleFilled/>} checkedChildren={<CheckCircleFilled/>}
unCheckedChildren={<CheckCircleOutlined/>} unCheckedChildren={<CheckCircleOutlined/>}
@@ -242,7 +267,7 @@ function TaskListComponent({
</Button> </Button>
</Space> </Space>
); );
}, [refetch, deleted, completed]); }, [refetch, deleted, completed, mine]);
return ( return (
<Card <Card

View File

@@ -12,10 +12,10 @@ import TaskListComponent from "./task-list.component.jsx";
import {notification} from "antd"; import {notification} from "antd";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
export default function TaskListContainer({bodyshop, titleTranslation ,query, relationshipType, relationshipId}) { export default function TaskListContainer({bodyshop, titleTranslation ,query, relationshipType, relationshipId, currentUser, onlyMine, parentJobId}) {
const {t} = useTranslation(); const {t} = useTranslation();
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
const {page, sortcolumn, sortorder, deleted, completed} = searchParams; const {page, sortcolumn, sortorder, deleted, completed, mine} = searchParams;
const {loading, error, data, refetch} = useQuery( const {loading, error, data, refetch} = useQuery(
query, query,
{ {
@@ -26,6 +26,7 @@ export default function TaskListContainer({bodyshop, titleTranslation ,query, r
[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
offset: page ? (page - 1) * pageLimit : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: pageLimit, limit: pageLimit,
order: [ order: [
@@ -147,6 +148,8 @@ export default function TaskListContainer({bodyshop, titleTranslation ,query, r
toggleDeletedStatus={toggleDeletedStatus} toggleDeletedStatus={toggleDeletedStatus}
relationshipType={relationshipType} relationshipType={relationshipType}
relationshipId={relationshipId} relationshipId={relationshipId}
onlyMine={onlyMine}
parentJobId={parentJobId}
/> />
); );
} }

View File

@@ -187,7 +187,7 @@ export function TaskUpsertModalComponent({
}, },
]} ]}
> >
<Select placeholder={t("tasks.labels.selectemployee")}> <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}

View File

@@ -59,6 +59,10 @@ export const QUERY_BILLS_BY_JOBID = gql`
name name
email email
} }
tasks {
id
due_date
}
order_date order_date
deliver_by deliver_by
return return

View File

@@ -100,6 +100,11 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
suspended suspended
est_ct_fn est_ct_fn
est_ct_ln est_ct_ln
tasks_aggregate(where: { completed: { _eq: false }, deleted: { _eq: false } }) {
aggregate {
count
}
}
} }
} }
`; `;
@@ -499,6 +504,11 @@ export const QUERY_JOB_COSTING_DETAILS = gql`
export const GET_JOB_BY_PK = gql` export const GET_JOB_BY_PK = gql`
query GET_JOB_BY_PK($id: uuid!) { query GET_JOB_BY_PK($id: uuid!) {
jobs_by_pk(id: $id) { jobs_by_pk(id: $id) {
tasks_aggregate(where: { completed: { _eq: false }, deleted: { _eq: false } }) {
aggregate {
count
}
}
actual_completion actual_completion
actual_delivery actual_delivery
actual_in actual_in

View File

@@ -97,6 +97,7 @@ export const QUERY_JOBLINE_TASKS_PAGINATED = gql`
$bodyshop: uuid! $bodyshop: uuid!
$deleted: Boolean $deleted: Boolean
$completed: Boolean $completed: Boolean
$assigned_to: String
$order: [tasks_order_by!]! $order: [tasks_order_by!]!
) { ) {
tasks( tasks(
@@ -107,6 +108,7 @@ export const QUERY_JOBLINE_TASKS_PAGINATED = gql`
joblineid: {_eq: $joblineid}, joblineid: {_eq: $joblineid},
bodyshopid: {_eq: $bodyshop}, bodyshopid: {_eq: $bodyshop},
deleted: {_eq: $deleted}, deleted: {_eq: $deleted},
assigned_to: {_eq: $assigned_to},
completed: {_eq: $completed} completed: {_eq: $completed}
} }
) { ) {
@@ -117,6 +119,7 @@ export const QUERY_JOBLINE_TASKS_PAGINATED = gql`
joblineid: {_eq: $joblineid}, joblineid: {_eq: $joblineid},
bodyshopid: {_eq: $bodyshop}, bodyshopid: {_eq: $bodyshop},
deleted: {_eq: $deleted}, deleted: {_eq: $deleted},
assigned_to: {_eq: $assigned_to},
completed: {_eq: $completed} completed: {_eq: $completed}
} }
) { ) {
@@ -137,6 +140,7 @@ export const QUERY_PARTSORDER_TASKS_PAGINATED = gql`
$bodyshop: uuid! $bodyshop: uuid!
$deleted: Boolean $deleted: Boolean
$completed: Boolean $completed: Boolean
$assigned_to: String
$order: [tasks_order_by!]! $order: [tasks_order_by!]!
) { ) {
tasks( tasks(
@@ -147,6 +151,7 @@ export const QUERY_PARTSORDER_TASKS_PAGINATED = gql`
partsorderid: {_eq: $partsorderid}, partsorderid: {_eq: $partsorderid},
bodyshopid: {_eq: $bodyshop}, bodyshopid: {_eq: $bodyshop},
deleted: {_eq: $deleted}, deleted: {_eq: $deleted},
assigned_to: {_eq: $assigned_to},
completed: {_eq: $completed} completed: {_eq: $completed}
} }
) { ) {
@@ -157,6 +162,7 @@ export const QUERY_PARTSORDER_TASKS_PAGINATED = gql`
partsorderid: {_eq: $partsorderid}, partsorderid: {_eq: $partsorderid},
bodyshopid: {_eq: $bodyshop}, bodyshopid: {_eq: $bodyshop},
deleted: {_eq: $deleted}, deleted: {_eq: $deleted},
assigned_to: {_eq: $assigned_to},
completed: {_eq: $completed} completed: {_eq: $completed}
} }
) { ) {
@@ -177,6 +183,7 @@ export const QUERY_BILL_TASKS_PAGINATED = gql`
$bodyshop: uuid! $bodyshop: uuid!
$deleted: Boolean $deleted: Boolean
$completed: Boolean $completed: Boolean
$assigned_to: String
$order: [tasks_order_by!]! $order: [tasks_order_by!]!
) { ) {
tasks( tasks(
@@ -187,6 +194,7 @@ export const QUERY_BILL_TASKS_PAGINATED = gql`
billid: {_eq: $billid}, billid: {_eq: $billid},
bodyshopid: {_eq: $bodyshop}, bodyshopid: {_eq: $bodyshop},
deleted: {_eq: $deleted}, deleted: {_eq: $deleted},
assigned_to: {_eq: $assigned_to},
completed: {_eq: $completed} completed: {_eq: $completed}
} }
) { ) {
@@ -197,6 +205,7 @@ export const QUERY_BILL_TASKS_PAGINATED = gql`
billid: {_eq: $billid}, billid: {_eq: $billid},
bodyshopid: {_eq: $bodyshop}, bodyshopid: {_eq: $bodyshop},
deleted: {_eq: $deleted}, deleted: {_eq: $deleted},
assigned_to: {_eq: $assigned_to},
completed: {_eq: $completed} completed: {_eq: $completed}
} }
) { ) {
@@ -217,6 +226,7 @@ export const QUERY_JOB_TASKS_PAGINATED = gql`
$bodyshop: uuid! $bodyshop: uuid!
$deleted: Boolean $deleted: Boolean
$completed: Boolean $completed: Boolean
$assigned_to: String
$order: [tasks_order_by!]! $order: [tasks_order_by!]!
) { ) {
tasks( tasks(
@@ -227,6 +237,7 @@ export const QUERY_JOB_TASKS_PAGINATED = gql`
jobid: {_eq: $jobid}, jobid: {_eq: $jobid},
bodyshopid: {_eq: $bodyshop}, bodyshopid: {_eq: $bodyshop},
deleted: {_eq: $deleted}, deleted: {_eq: $deleted},
assigned_to: {_eq: $assigned_to},
completed: {_eq: $completed} completed: {_eq: $completed}
} }
) { ) {
@@ -237,6 +248,7 @@ export const QUERY_JOB_TASKS_PAGINATED = gql`
jobid: {_eq: $jobid}, jobid: {_eq: $jobid},
bodyshopid: {_eq: $bodyshop}, bodyshopid: {_eq: $bodyshop},
deleted: {_eq: $deleted}, deleted: {_eq: $deleted},
assigned_to: {_eq: $assigned_to},
completed: {_eq: $completed} completed: {_eq: $completed}
} }
) { ) {

View File

@@ -8,7 +8,7 @@ import Icon, {
SyncOutlined, SyncOutlined,
ToolFilled ToolFilled
} from "@ant-design/icons"; } from "@ant-design/icons";
import {Button, Divider, Form, notification, Space, Tabs} from "antd"; import {Badge, Button, Divider, Form, notification, Space, Tabs} from "antd";
import {PageHeader} from "@ant-design/pro-layout"; import {PageHeader} from "@ant-design/pro-layout";
import Axios from "axios"; import Axios from "axios";
@@ -55,7 +55,7 @@ import ScheduleJobModalContainer
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";
import {selectBodyshop} from "../../redux/user/user.selectors"; 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";
@@ -69,7 +69,8 @@ import {QUERY_JOB_TASKS_PAGINATED} from "../../graphql/tasks.queries.js";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
jobRO: selectJobReadOnly jobRO: selectJobReadOnly,
currentUser: selectCurrentUser
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) => dispatch(setModalContext({ setPrintCenterContext: (context) => dispatch(setModalContext({
@@ -90,6 +91,7 @@ export function JobsDetailPage({
job, job,
mutationUpdateJob, mutationUpdateJob,
handleSubmit, handleSubmit,
currentUser,
insertAuditTrail, insertAuditTrail,
refetch refetch
}) { }) {
@@ -391,8 +393,10 @@ export function JobsDetailPage({
{ {
key: 'tasks', key: 'tasks',
icon: <FaTasks/>, icon: <FaTasks/>,
label: t("menus.jobsdetail.tasks"), label: <Space direction='horizontal'>
children: <TaskListContainer bodyshop={bodyshop} relationshipType={'jobid'} relationshipId={job.id} query={QUERY_JOB_TASKS_PAGINATED} titleTranslation='tasks.titles.job_tasks'/> {t("jobs.labels.tasks")}{job.tasks_aggregate.aggregate.count > 0 && <Badge count={job.tasks_aggregate.aggregate.count} />}
</Space>,
children: <TaskListContainer currentUser={currentUser} bodyshop={bodyshop} relationshipType={'jobid'} relationshipId={job.id} query={QUERY_JOB_TASKS_PAGINATED} titleTranslation='tasks.titles.job_tasks'/>
}, },
]} ]}
/> />

View File

@@ -6,7 +6,7 @@ export default function TasksPageComponent({bodyshop, currentUser}) {
return ( return (
<div> <div>
<TaskListContainer relationshipId={currentUser.email} relationshipType={'user'} query={QUERY_MY_TASKS_PAGINATED} bodyshop={bodyshop} titleTranslation={'tasks.titles.my_tasks'} <TaskListContainer onlyMine={true} relationshipId={currentUser.email} relationshipType={'user'} query={QUERY_MY_TASKS_PAGINATED} bodyshop={bodyshop} titleTranslation={'tasks.titles.my_tasks'}
currentUser={currentUser}/> currentUser={currentUser}/>
</div> </div>
); );

View File

@@ -1524,7 +1524,8 @@
"voiding": "Error voiding Job. {{error}}" "voiding": "Error voiding Job. {{error}}"
}, },
"fields": { "fields": {
"actual_completion": "Actual Completion", "active_tasks": "Active Tasks",
"actual_completion": "Actual Completion",
"actual_delivery": "Actual Delivery", "actual_delivery": "Actual Delivery",
"actual_in": "Actual In", "actual_in": "Actual In",
"adjustment_bottom_line": "Adjustments", "adjustment_bottom_line": "Adjustments",
@@ -1840,7 +1841,8 @@
"appointmentconfirmation": "Send confirmation to customer?", "appointmentconfirmation": "Send confirmation to customer?",
"associationwarning": "Any changes to associations will require updating the data from the new parent record to the Job.", "associationwarning": "Any changes to associations will require updating the data from the new parent record to the Job.",
"audit": "Audit Trail", "audit": "Audit Trail",
"available": "Available", "tasks": "Tasks",
"available": "Available",
"availablejobs": "Available Jobs", "availablejobs": "Available Jobs",
"ca_bc_pvrt": { "ca_bc_pvrt": {
"days": "Days", "days": "Days",
@@ -2121,6 +2123,7 @@
}, },
"titles": { "titles": {
"job_tasks": "Job Tasks", "job_tasks": "Job Tasks",
"mine": "My Tasks",
"my_tasks": "My Tasks", "my_tasks": "My Tasks",
"completed": "Completed Tasks", "completed": "Completed Tasks",
"deleted": "Deleted Tasks" "deleted": "Deleted Tasks"
@@ -2130,14 +2133,17 @@
"complete": "Complete", "complete": "Complete",
"delete": "Delete", "delete": "Delete",
"edit": "Edit", "edit": "Edit",
"refresh": "Refresh" "refresh": "Refresh",
"myTasks": "Mine",
"allTasks": "All"
}, },
"placeholders": { "placeholders": {
"description": "Enter a description", "description": "Enter a description",
"jobid": "Select a Job", "jobid": "Select a Job",
"joblineid": "Select a Job Line", "joblineid": "Select a Job Line",
"partsorderid": "Select a Parts Order", "partsorderid": "Select a Parts Order",
"billid": "Select a Bill" "billid": "Select a Bill",
"assigned_to": "Select an Employee"
}, },
"fields": { "fields": {
"priorities": { "priorities": {
@@ -3127,6 +3133,7 @@
} }
}, },
"titles": { "titles": {
"tasks": "Tasks",
"accounting-payables": "Payables | {{app}}", "accounting-payables": "Payables | {{app}}",
"accounting-payments": "Payments | {{app}}", "accounting-payments": "Payments | {{app}}",
"accounting-receivables": "Receivables | {{app}}", "accounting-receivables": "Receivables | {{app}}",

View File

@@ -1524,6 +1524,7 @@
"voiding": "" "voiding": ""
}, },
"fields": { "fields": {
"active_tasks": "",
"actual_completion": "Realización real", "actual_completion": "Realización real",
"actual_delivery": "Entrega real", "actual_delivery": "Entrega real",
"actual_in": "Real en", "actual_in": "Real en",
@@ -1840,7 +1841,8 @@
"appointmentconfirmation": "¿Enviar confirmación al cliente?", "appointmentconfirmation": "¿Enviar confirmación al cliente?",
"associationwarning": "", "associationwarning": "",
"audit": "", "audit": "",
"available": "", "tasks": "",
"available": "",
"availablejobs": "", "availablejobs": "",
"ca_bc_pvrt": { "ca_bc_pvrt": {
"days": "", "days": "",
@@ -2120,6 +2122,8 @@
"edit": "" "edit": ""
}, },
"titles": { "titles": {
"job_tasks": "",
"ny_tasks": "",
"completed": "", "completed": "",
"deleted": "" "deleted": ""
}, },
@@ -2128,14 +2132,17 @@
"complete": "", "complete": "",
"delete": "", "delete": "",
"edit": "", "edit": "",
"refresh":"" "refresh":"",
"myTasks": "",
"allTasks": ""
}, },
"placeholders": { "placeholders": {
"description": "", "description": "",
"jobid": "", "jobid": "",
"joblineid": "", "joblineid": "",
"partsorderid": "", "partsorderid": "",
"billid": "" "billid": "",
"assigned_to": ""
}, },
"fields": { "fields": {
"priorities": { "priorities": {
@@ -3125,6 +3132,7 @@
} }
}, },
"titles": { "titles": {
"tasks": "",
"accounting-payables": "", "accounting-payables": "",
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",

View File

@@ -1524,6 +1524,7 @@
"voiding": "" "voiding": ""
}, },
"fields": { "fields": {
"active_tasks": "",
"actual_completion": "Achèvement réel", "actual_completion": "Achèvement réel",
"actual_delivery": "Livraison réelle", "actual_delivery": "Livraison réelle",
"actual_in": "En réel", "actual_in": "En réel",
@@ -1840,7 +1841,8 @@
"appointmentconfirmation": "Envoyer une confirmation au client?", "appointmentconfirmation": "Envoyer une confirmation au client?",
"associationwarning": "", "associationwarning": "",
"audit": "", "audit": "",
"available": "", "tasks": "",
"available": "",
"availablejobs": "", "availablejobs": "",
"ca_bc_pvrt": { "ca_bc_pvrt": {
"days": "", "days": "",
@@ -2120,6 +2122,8 @@
"edit": "" "edit": ""
}, },
"titles": { "titles": {
"job_tasks": "",
"ny_tasks": "",
"completed": "", "completed": "",
"deleted": "" "deleted": ""
}, },
@@ -2128,14 +2132,17 @@
"complete": "", "complete": "",
"delete": "", "delete": "",
"edit": "", "edit": "",
"refresh":"" "refresh":"",
"myTasks": "",
"allTasks": ""
}, },
"placeholders": { "placeholders": {
"description": "", "description": "",
"jobid": "", "jobid": "",
"joblineid": "", "joblineid": "",
"partsorderid": "", "partsorderid": "",
"billid": "" "billid": "",
"assigned_to": ""
}, },
"fields": { "fields": {
"priorities": { "priorities": {
@@ -3129,6 +3136,7 @@
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",
"app": "", "app": "",
"tasks": "",
"bc": { "bc": {
"tasks": "", "tasks": "",
"accounting-payables": "", "accounting-payables": "",