264 lines
7.2 KiB
JavaScript
264 lines
7.2 KiB
JavaScript
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 dayjs from "../../utils/day";
|
|
import {
|
|
CheckCircleFilled,
|
|
CheckCircleOutlined,
|
|
DeleteFilled,
|
|
DeleteOutlined,
|
|
EditFilled,
|
|
ExclamationCircleFilled,
|
|
PlusCircleFilled,
|
|
SyncOutlined
|
|
} from "@ant-design/icons";
|
|
import {DateFormatter} from "../../utils/DateFormatter.jsx";
|
|
import {connect} from 'react-redux';
|
|
import {setModalContext} from '../../redux/modals/modals.actions';
|
|
|
|
/**
|
|
* Task List Component
|
|
* @param dueDate
|
|
* @returns {Element}
|
|
* @constructor
|
|
*/
|
|
const DueDateRecord = ({dueDate}) => {
|
|
if (dueDate) {
|
|
const dueDateDayjs = dayjs(dueDate);
|
|
const relativeDueDate = dueDateDayjs.fromNow();
|
|
const today = dayjs();
|
|
|
|
if (dueDateDayjs.isAfter(today)) {
|
|
return <div title={relativeDueDate} style={{color: 'red'}}>
|
|
<DateFormatter>{dueDate}</DateFormatter></div>;
|
|
} else {
|
|
return <div title={relativeDueDate}><DateFormatter>{dueDate}</DateFormatter></div>;
|
|
}
|
|
} else {
|
|
return <div>N/A</div>;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Priority Label Component
|
|
* @param priority
|
|
* @returns {Element}
|
|
* @constructor
|
|
*/
|
|
const PriorityLabel = ({priority}) => {
|
|
switch(priority) {
|
|
case 1:
|
|
return <div>
|
|
High <ExclamationCircleFilled style={{marginLeft: '5px', color: 'red'}}/>
|
|
</div>;
|
|
case 2:
|
|
return <div>
|
|
Medium <ExclamationCircleFilled style={{marginLeft: '5px', color: 'yellow'}}/>
|
|
</div>;
|
|
case 3:
|
|
return <div>
|
|
Low <ExclamationCircleFilled style={{marginLeft: '5px', color: 'green'}}/>
|
|
</div>;
|
|
default:
|
|
return <div>
|
|
None <ExclamationCircleFilled style={{marginLeft: '5px', color: 'black'}}/>
|
|
</div>;
|
|
}
|
|
}
|
|
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
// Existing dispatch props...
|
|
setTaskUpsertContext: (context) => dispatch(setModalContext({context, modal: 'taskUpsert'})),
|
|
});
|
|
|
|
const mapStateToProps = (state) => ({
|
|
// Existing state props...
|
|
});
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(TaskListComponent);
|
|
|
|
function TaskListComponent({
|
|
loading,
|
|
tasks,
|
|
total,
|
|
refetch,
|
|
toggleCompletedStatus,
|
|
setTaskUpsertContext,
|
|
}) {
|
|
const {t} = useTranslation();
|
|
|
|
const search = queryString.parse(useLocation().search);
|
|
|
|
// Extract Query Params
|
|
const {
|
|
page,
|
|
sortcolumn,
|
|
sortorder,
|
|
deleted,
|
|
completed
|
|
} = search;
|
|
|
|
const history = useNavigate();
|
|
|
|
const columns = [
|
|
{
|
|
title: t("tasks.fields.job.ro_number"),
|
|
dataIndex: ["job", "ro_number"],
|
|
key: "job.ro_number",
|
|
width: '5%',
|
|
render: (text, record) => (
|
|
<Link to={`/manage/jobs/${record.job.id}`}>
|
|
{record.job.ro_number}
|
|
</Link>
|
|
),
|
|
},
|
|
{
|
|
title: t("tasks.fields.title"),
|
|
dataIndex: "title",
|
|
key: "title",
|
|
sorter: true,
|
|
sortOrder: sortcolumn === "title" && sortorder,
|
|
},
|
|
{
|
|
title: t("tasks.fields.description"),
|
|
dataIndex: "description",
|
|
key: "description",
|
|
sorter: true,
|
|
sortOrder: sortcolumn === "description" && sortorder,
|
|
},
|
|
{
|
|
title: t("tasks.fields.due_date"),
|
|
dataIndex: "due_date",
|
|
key: "due_date",
|
|
sorter: true,
|
|
sortOrder: sortcolumn === "due_date" && sortorder,
|
|
width: '5%',
|
|
render: (text, record) => <DueDateRecord dueDate={record.due_date} />,
|
|
},
|
|
{
|
|
title: t("tasks.fields.priority"),
|
|
dataIndex: "priority",
|
|
key: "priority",
|
|
sorter: true,
|
|
sortOrder: sortcolumn === "priority" && sortorder,
|
|
width: '5%',
|
|
render: (text, record) => <PriorityLabel priority={record.priority}
|
|
/>
|
|
},
|
|
{
|
|
title: t("tasks.fields.actions"),
|
|
key: "toggleCompleted",
|
|
width: '5%',
|
|
render: (text, record) => (
|
|
<Space direction='horizontal'>
|
|
<Button title={t('tasks.actions.edit')} onClick={() => {
|
|
setTaskUpsertContext({
|
|
actions: {},
|
|
context: {
|
|
existingTask: record,
|
|
},
|
|
});
|
|
}}>
|
|
<EditFilled/>
|
|
</Button>
|
|
<Button title={t('tasks.actions.toggle_completed')}
|
|
onClick={() => toggleCompletedStatus(record.id, record.completed)}>
|
|
{record.completed ? <CheckCircleOutlined/> :
|
|
<CheckCircleFilled/>}
|
|
</Button>
|
|
<Button title={t('tasks.actions.toggle_deleted')}
|
|
onClick={() => toggleDeletedStatus(record.id, record.deleted)}>
|
|
{record.deleted ? <DeleteFilled/> : <DeleteOutlined/>}
|
|
</Button>
|
|
</Space>
|
|
),
|
|
},
|
|
];
|
|
|
|
const [state, setState] = useState({
|
|
sortedInfo: {},
|
|
filteredInfo: {text: ""},
|
|
});
|
|
|
|
const handleCreateTask = () => {
|
|
setTaskUpsertContext({
|
|
actions: {},
|
|
context: {},
|
|
});
|
|
};
|
|
|
|
const handleTableChange = (pagination, filters, sorter) => {
|
|
setState({...state, filteredInfo: filters, sortedInfo: sorter});
|
|
search.page = pagination.current;
|
|
search.sortcolumn = sorter.columnKey;
|
|
search.sortorder = sorter.order;
|
|
history({search: queryString.stringify(search)});
|
|
};
|
|
|
|
const handleSwitchChange = (param, value) => {
|
|
if (value) {
|
|
search[param] = "true";
|
|
} else {
|
|
delete search[param];
|
|
}
|
|
history({search: queryString.stringify(search)});
|
|
};
|
|
|
|
/**
|
|
* Extra actions for the tasks
|
|
* @type {Function}
|
|
*/
|
|
const tasksExtra = useCallback(() => {
|
|
return (
|
|
<Space direction='horizontal'>
|
|
<Switch
|
|
checkedChildren={<CheckCircleFilled/>}
|
|
unCheckedChildren={<CheckCircleOutlined/>}
|
|
title={t('tasks.titles.completed')}
|
|
checked={completed === "true"}
|
|
onChange={(value) => handleSwitchChange('completed', value)}
|
|
/>
|
|
<Switch
|
|
checkedChildren={<DeleteOutlined/>}
|
|
unCheckedChildren={<DeleteFilled/>}
|
|
title={t('tasks.titles.deleted')}
|
|
checked={deleted === "true"}
|
|
onChange={(value) => handleSwitchChange('deleted', value)}
|
|
/>
|
|
<Button title={t('tasks.titles.create')} onClick={handleCreateTask}>
|
|
<PlusCircleFilled/>{t('tasks.buttons.create')}
|
|
</Button>
|
|
<Button title={t('tasks.titles.refresh')}
|
|
onClick={() => refetch()}>
|
|
<SyncOutlined/>
|
|
</Button>
|
|
</Space>
|
|
);
|
|
}, [refetch, deleted, completed]);
|
|
|
|
return (
|
|
<Card
|
|
title={t("menus.header.my_tasks")}
|
|
extra={tasksExtra()}
|
|
>
|
|
<Table
|
|
loading={loading}
|
|
pagination={{
|
|
position: "top",
|
|
pageSize: pageLimit,
|
|
current: parseInt(page || 1),
|
|
total: total,
|
|
}}
|
|
columns={columns}
|
|
rowKey="id"
|
|
scroll={{x: true}}
|
|
dataSource={tasks}
|
|
onChange={handleTableChange}
|
|
/>
|
|
</Card>
|
|
);
|
|
}
|