@@ -1,9 +1,9 @@
|
||||
import {Button, Card, Space, Switch, Table} from "antd";
|
||||
import { Button, Card, Space, Switch, Table } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React, {useCallback, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import { pageLimit } from "../../utils/config";
|
||||
import dayjs from "../../utils/day";
|
||||
import {
|
||||
CheckCircleFilled,
|
||||
@@ -15,9 +15,9 @@ import {
|
||||
PlusCircleFilled,
|
||||
SyncOutlined
|
||||
} from "@ant-design/icons";
|
||||
import {DateFormatter} from "../../utils/DateFormatter.jsx";
|
||||
import {connect} from 'react-redux';
|
||||
import {setModalContext} from '../../redux/modals/modals.actions';
|
||||
import { DateFormatter } from "../../utils/DateFormatter.jsx";
|
||||
import { connect } from "react-redux";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
|
||||
/**
|
||||
* Task List Component
|
||||
@@ -25,7 +25,7 @@ import {setModalContext} from '../../redux/modals/modals.actions';
|
||||
* @returns {Element}
|
||||
* @constructor
|
||||
*/
|
||||
const DueDateRecord = ({dueDate}) => {
|
||||
const DueDateRecord = ({ dueDate }) => {
|
||||
if (!dueDate) return <></>;
|
||||
|
||||
const dueDateDayjs = dayjs(dueDate);
|
||||
@@ -33,11 +33,11 @@ const DueDateRecord = ({dueDate}) => {
|
||||
const isBeforeToday = dueDateDayjs.isBefore(dayjs());
|
||||
|
||||
return (
|
||||
<div title={relativeDueDate} style={{color: isBeforeToday ? 'red' : 'black'}}>
|
||||
<div title={relativeDueDate} style={{ color: isBeforeToday ? "red" : "black" }}>
|
||||
<DateFormatter>{dueDate}</DateFormatter>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Priority Label Component
|
||||
@@ -45,30 +45,38 @@ const DueDateRecord = ({dueDate}) => {
|
||||
* @returns {Element}
|
||||
* @constructor
|
||||
*/
|
||||
const PriorityLabel = ({priority}) => {
|
||||
const PriorityLabel = ({ priority }) => {
|
||||
switch (priority) {
|
||||
case 1:
|
||||
return <div>
|
||||
High <ExclamationCircleFilled style={{marginLeft: '5px', color: 'red'}}/>
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
High <ExclamationCircleFilled style={{ marginLeft: "5px", color: "red" }} />
|
||||
</div>
|
||||
);
|
||||
case 2:
|
||||
return <div>
|
||||
Medium <ExclamationCircleFilled style={{marginLeft: '5px', color: 'yellow'}}/>
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
Medium <ExclamationCircleFilled style={{ marginLeft: "5px", color: "yellow" }} />
|
||||
</div>
|
||||
);
|
||||
case 3:
|
||||
return <div>
|
||||
Low <ExclamationCircleFilled style={{marginLeft: '5px', color: 'green'}}/>
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
Low <ExclamationCircleFilled style={{ marginLeft: "5px", color: "green" }} />
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return <div>
|
||||
None <ExclamationCircleFilled style={{marginLeft: '5px', color: 'black'}}/>
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
None <ExclamationCircleFilled style={{ marginLeft: "5px", color: "black" }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
// Existing dispatch props...
|
||||
setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})),
|
||||
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||
});
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
@@ -78,69 +86,59 @@ const mapStateToProps = (state) => ({
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TaskListComponent);
|
||||
|
||||
function TaskListComponent({
|
||||
bodyshop,
|
||||
loading,
|
||||
tasks,
|
||||
total,
|
||||
titleTranslation,
|
||||
refetch,
|
||||
toggleCompletedStatus,
|
||||
setTaskUpsertContext,
|
||||
toggleDeletedStatus,
|
||||
relationshipType,
|
||||
relationshipId,
|
||||
onlyMine,
|
||||
parentJobId,
|
||||
showRo = true,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
bodyshop,
|
||||
loading,
|
||||
tasks,
|
||||
total,
|
||||
titleTranslation,
|
||||
refetch,
|
||||
toggleCompletedStatus,
|
||||
setTaskUpsertContext,
|
||||
toggleDeletedStatus,
|
||||
relationshipType,
|
||||
relationshipId,
|
||||
onlyMine,
|
||||
parentJobId,
|
||||
showRo = true
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const search = queryString.parse(useLocation().search);
|
||||
|
||||
// Extract Query Params
|
||||
const {
|
||||
page,
|
||||
sortcolumn,
|
||||
sortorder,
|
||||
deleted,
|
||||
completed,
|
||||
mine
|
||||
} = search;
|
||||
const { page, sortcolumn, sortorder, deleted, completed, mine } = search;
|
||||
|
||||
const history = useNavigate();
|
||||
const columns = [];
|
||||
|
||||
if (!onlyMine) {
|
||||
columns.push(
|
||||
{
|
||||
title: t("tasks.fields.assigned_to"),
|
||||
dataIndex: "assigned_to",
|
||||
key: "assigned_to",
|
||||
width: '8%',
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "assigned_to" && sortorder,
|
||||
render: (text, record) => {
|
||||
const employee = bodyshop?.employees?.find(e => e.user_email === record.assigned_to);
|
||||
return employee ? `${employee.first_name} ${employee.last_name}` : t("general.labels.na");
|
||||
}
|
||||
columns.push({
|
||||
title: t("tasks.fields.assigned_to"),
|
||||
dataIndex: "assigned_to",
|
||||
key: "assigned_to",
|
||||
width: "8%",
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "assigned_to" && sortorder,
|
||||
render: (text, record) => {
|
||||
const employee = bodyshop?.employees?.find((e) => e.user_email === record.assigned_to);
|
||||
return employee ? `${employee.first_name} ${employee.last_name}` : t("general.labels.na");
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (showRo) {
|
||||
columns.push(
|
||||
{
|
||||
title: t("tasks.fields.job.ro_number"),
|
||||
dataIndex: ["job", "ro_number"],
|
||||
key: "job.ro_number",
|
||||
width: '8%',
|
||||
render: (text, record) =>
|
||||
record.job
|
||||
? <Link
|
||||
to={`/manage/jobs/${record.job.id}`}>{record.job.ro_number || t("general.labels.na")}</Link>
|
||||
: t("general.labels.na")
|
||||
}
|
||||
);
|
||||
columns.push({
|
||||
title: t("tasks.fields.job.ro_number"),
|
||||
dataIndex: ["job", "ro_number"],
|
||||
key: "job.ro_number",
|
||||
width: "8%",
|
||||
render: (text, record) =>
|
||||
record.job ? (
|
||||
<Link to={`/manage/jobs/${record.job.id}`}>{record.job.ro_number || t("general.labels.na")}</Link>
|
||||
) : (
|
||||
t("general.labels.na")
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
columns.push(
|
||||
@@ -148,44 +146,47 @@ function TaskListComponent({
|
||||
title: t("tasks.fields.jobline"),
|
||||
dataIndex: ["jobline", "id"],
|
||||
key: "jobline.id",
|
||||
width: '8%',
|
||||
render: (text, record) => record?.jobline?.line_desc || ''
|
||||
width: "8%",
|
||||
render: (text, record) => record?.jobline?.line_desc || ""
|
||||
},
|
||||
{
|
||||
title: t("tasks.fields.parts_order"),
|
||||
dataIndex: ["parts_order", "id"],
|
||||
key: "part_order.id",
|
||||
width: '8%',
|
||||
width: "8%",
|
||||
render: (text, record) =>
|
||||
record.parts_order
|
||||
? <Link
|
||||
to={`/manage/jobs/${record.job.id}?partsorderid=${record.parts_order.id}&tab=partssublet`}>
|
||||
record.parts_order ? (
|
||||
<Link 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.name}`
|
||||
: t("general.labels.na")}
|
||||
</Link>
|
||||
: ''
|
||||
) : (
|
||||
""
|
||||
)
|
||||
},
|
||||
{
|
||||
title: t("tasks.fields.bill"),
|
||||
dataIndex: ["bill", "id"],
|
||||
key: "bill.id",
|
||||
width: '8%',
|
||||
width: "8%",
|
||||
render: (text, record) =>
|
||||
record.bill
|
||||
? <Link to={`/manage/jobs/${record.job.id}?billid=${record.bill.id}&tab=partssublet`}>
|
||||
record.bill ? (
|
||||
<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.name}`
|
||||
: t("general.labels.na")}
|
||||
</Link>
|
||||
: ''
|
||||
) : (
|
||||
""
|
||||
)
|
||||
},
|
||||
{
|
||||
title: t("tasks.fields.title"),
|
||||
dataIndex: "title",
|
||||
key: "title",
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "title" && sortorder,
|
||||
sortOrder: sortcolumn === "title" && sortorder
|
||||
},
|
||||
{
|
||||
title: t("tasks.fields.due_date"),
|
||||
@@ -193,8 +194,8 @@ function TaskListComponent({
|
||||
key: "due_date",
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "due_date" && sortorder,
|
||||
width: '8%',
|
||||
render: (text, record) => <DueDateRecord dueDate={record.due_date}/>,
|
||||
width: "8%",
|
||||
render: (text, record) => <DueDateRecord dueDate={record.due_date} />
|
||||
},
|
||||
{
|
||||
title: t("tasks.fields.remind_at"),
|
||||
@@ -202,8 +203,8 @@ function TaskListComponent({
|
||||
key: "remind_at",
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "remind_at" && sortorder,
|
||||
width: '8%',
|
||||
render: (text, record) => <DueDateRecord dueDate={record.remind_at}/>,
|
||||
width: "8%",
|
||||
render: (text, record) => <DueDateRecord dueDate={record.remind_at} />
|
||||
},
|
||||
{
|
||||
title: t("tasks.fields.priority"),
|
||||
@@ -211,75 +212,82 @@ function TaskListComponent({
|
||||
key: "priority",
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "priority" && sortorder,
|
||||
width: '8%',
|
||||
render: (text, record) => <PriorityLabel priority={record.priority}
|
||||
/>
|
||||
width: "8%",
|
||||
render: (text, record) => <PriorityLabel priority={record.priority} />
|
||||
},
|
||||
{
|
||||
title: t("tasks.fields.actions"),
|
||||
key: "toggleCompleted",
|
||||
width: '5%',
|
||||
width: "5%",
|
||||
render: (text, record) => (
|
||||
<Space direction='horizontal'>
|
||||
<Button title={t('tasks.buttons.edit')} onClick={() => {
|
||||
setTaskUpsertContext({
|
||||
context: {
|
||||
existingTask: record,
|
||||
},
|
||||
});
|
||||
}}>
|
||||
<EditFilled/>
|
||||
<Space direction="horizontal">
|
||||
<Button
|
||||
title={t("tasks.buttons.edit")}
|
||||
onClick={() => {
|
||||
setTaskUpsertContext({
|
||||
context: {
|
||||
existingTask: record
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<EditFilled />
|
||||
</Button>
|
||||
<Button title={t('tasks.buttons.complete')}
|
||||
onClick={() => toggleCompletedStatus(record.id, record.completed)}>
|
||||
{record.completed ? <CheckCircleOutlined/> :
|
||||
<CheckCircleFilled/>}
|
||||
<Button
|
||||
title={t("tasks.buttons.complete")}
|
||||
onClick={() => toggleCompletedStatus(record.id, record.completed)}
|
||||
>
|
||||
{record.completed ? <CheckCircleOutlined /> : <CheckCircleFilled />}
|
||||
</Button>
|
||||
<Button title={t('tasks.buttons.delete')}
|
||||
onClick={() => toggleDeletedStatus(record.id, record.deleted)}>
|
||||
{record.deleted ? <DeleteFilled/> : <DeleteOutlined/>}
|
||||
<Button title={t("tasks.buttons.delete")} onClick={() => toggleDeletedStatus(record.id, record.deleted)}>
|
||||
{record.deleted ? <DeleteFilled /> : <DeleteOutlined />}
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: {text: ""},
|
||||
filteredInfo: { text: "" }
|
||||
});
|
||||
|
||||
const handleCreateTask = useCallback(() => {
|
||||
setTaskUpsertContext({
|
||||
actions: {},
|
||||
context: {
|
||||
jobid: parentJobId,
|
||||
[relationshipType]: relationshipId,
|
||||
},
|
||||
});
|
||||
}, [ parentJobId, relationshipId, relationshipType, setTaskUpsertContext]);
|
||||
setTaskUpsertContext({
|
||||
actions: {},
|
||||
context: {
|
||||
jobid: parentJobId,
|
||||
[relationshipType]: relationshipId
|
||||
}
|
||||
});
|
||||
}, [parentJobId, relationshipId, relationshipType, setTaskUpsertContext]);
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({...state, filteredInfo: filters, sortedInfo: sorter});
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
search.page = pagination.current;
|
||||
search.sortcolumn = sorter.columnKey;
|
||||
search.sortorder = sorter.order;
|
||||
history({search: queryString.stringify(search)});
|
||||
history({ search: queryString.stringify(search) });
|
||||
};
|
||||
|
||||
const handleSwitchChange = useCallback((param, value) => {
|
||||
if (value) {
|
||||
search[param] = "true";
|
||||
} else {
|
||||
delete search[param];
|
||||
}
|
||||
history({search: queryString.stringify(search)});
|
||||
}, [history, search]);
|
||||
const handleSwitchChange = useCallback(
|
||||
(param, value) => {
|
||||
if (value) {
|
||||
search[param] = "true";
|
||||
} else {
|
||||
delete search[param];
|
||||
}
|
||||
history({ search: queryString.stringify(search) });
|
||||
},
|
||||
[history, search]
|
||||
);
|
||||
|
||||
const expandableRow = (record) => {
|
||||
return <Card title={t('tasks.fields.description')} size='small'>
|
||||
{record.description}
|
||||
</Card>
|
||||
return (
|
||||
<Card title={t("tasks.fields.description")} size="small">
|
||||
{record.description}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -288,46 +296,43 @@ function TaskListComponent({
|
||||
*/
|
||||
const tasksExtra = useCallback(() => {
|
||||
return (
|
||||
<Space direction='horizontal'>
|
||||
<Space direction="horizontal">
|
||||
{!onlyMine && (
|
||||
<Switch
|
||||
checkedChildren={t('tasks.buttons.myTasks')}
|
||||
unCheckedChildren={t('tasks.buttons.allTasks')}
|
||||
title={t('tasks.titles.mine')}
|
||||
checkedChildren={t("tasks.buttons.myTasks")}
|
||||
unCheckedChildren={t("tasks.buttons.allTasks")}
|
||||
title={t("tasks.titles.mine")}
|
||||
checked={mine === "true"}
|
||||
onChange={(value) => handleSwitchChange('mine', value)}
|
||||
onChange={(value) => handleSwitchChange("mine", value)}
|
||||
/>
|
||||
)}
|
||||
<Switch
|
||||
checkedChildren={<CheckCircleFilled/>}
|
||||
unCheckedChildren={<CheckCircleOutlined/>}
|
||||
title={t('tasks.titles.completed')}
|
||||
checkedChildren={<CheckCircleFilled />}
|
||||
unCheckedChildren={<CheckCircleOutlined />}
|
||||
title={t("tasks.titles.completed")}
|
||||
checked={completed === "true"}
|
||||
onChange={(value) => handleSwitchChange('completed', value)}
|
||||
onChange={(value) => handleSwitchChange("completed", value)}
|
||||
/>
|
||||
<Switch
|
||||
checkedChildren={<DeleteOutlined/>}
|
||||
unCheckedChildren={<DeleteFilled/>}
|
||||
title={t('tasks.titles.deleted')}
|
||||
checkedChildren={<DeleteOutlined />}
|
||||
unCheckedChildren={<DeleteFilled />}
|
||||
title={t("tasks.titles.deleted")}
|
||||
checked={deleted === "true"}
|
||||
onChange={(value) => handleSwitchChange('deleted', value)}
|
||||
onChange={(value) => handleSwitchChange("deleted", value)}
|
||||
/>
|
||||
<Button title={t('tasks.buttons.create')} onClick={handleCreateTask}>
|
||||
<PlusCircleFilled/>{t('tasks.buttons.create')}
|
||||
<Button title={t("tasks.buttons.create")} onClick={handleCreateTask}>
|
||||
<PlusCircleFilled />
|
||||
{t("tasks.buttons.create")}
|
||||
</Button>
|
||||
<Button title={t('tasks.buttons.refresh')}
|
||||
onClick={() => refetch()}>
|
||||
<SyncOutlined/>
|
||||
<Button title={t("tasks.buttons.refresh")} onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}, [refetch, deleted, completed, mine, onlyMine, t, handleSwitchChange, handleCreateTask]);
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={titleTranslation}
|
||||
extra={tasksExtra()}
|
||||
>
|
||||
<Card title={titleTranslation} extra={tasksExtra()}>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{
|
||||
@@ -335,16 +340,16 @@ function TaskListComponent({
|
||||
current: parseInt(page || 1),
|
||||
total: total,
|
||||
responsive: true,
|
||||
showQuickJumper: true,
|
||||
showQuickJumper: true
|
||||
}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
scroll={{x: true}}
|
||||
scroll={{ x: true }}
|
||||
dataSource={tasks}
|
||||
onChange={handleTableChange}
|
||||
expandable={{
|
||||
expandedRowRender: expandableRow,
|
||||
rowExpandable: record => record.description,
|
||||
rowExpandable: (record) => record.description
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user