- Progress Commit

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-03-20 18:18:24 -04:00
parent 27c24619c3
commit f31ae9ac6d
11 changed files with 488 additions and 98 deletions

View File

@@ -1,74 +1,136 @@
import {SyncOutlined} from "@ant-design/icons";
import {Button, Card, Input, Space, Table, Typography} from "antd";
import {Button, Card, Space, Switch, Table} from "antd";
import queryString from "query-string";
import React, {useState} from "react";
import React, {useCallback, useState} from "react";
import {useTranslation} from "react-i18next";
import {useLocation, useNavigate} from "react-router-dom";
import {pageLimit} from "../../utils/config";
import dayjs from "../../utils/day";
import {
CheckCircleFilled,
CheckCircleOutlined,
DeleteFilled,
DeleteOutlined, ExclamationCircleFilled,
SyncOutlined
} from "@ant-design/icons";
const DueDateRecord = ({dueDate}) => {
if (dueDate) {
const dueDateFormatted = dayjs(dueDate).format("YYYY-MM-DD");
const relativeDueDate = dayjs(dueDate).fromNow();
const today = dayjs().format("YYYY-MM-DD");
if (dueDateFormatted < today) {
return <div title={relativeDueDate} style={{color: 'red'}}>{dueDateFormatted}</div>;
} else {
return <div title={relativeDueDate}>{dueDateFormatted}</div>;
}
} else {
return <div>N/A</div>;
}
}
const PriorityLabel = ({priority}) => {
switch(priority) {
case 1:
return <div>
<ExclamationCircleFilled style={{color: 'red'}} /> High
</div>;
case 2:
return <div>
<ExclamationCircleFilled style={{ color: 'yellow' }} /> Medium
</div>;
case 3:
return <div>
<ExclamationCircleFilled style={{ color: 'green' }} /> Low
</div>;
default:
return <div>
<ExclamationCircleFilled style={{ color: 'black' }} /> None
</div>;
}
}
export default function TaskListComponent({
loading,
tasks,
total,
refetch,
toggleCompletedStatus,
}) {
const {t} = useTranslation();
const search = queryString.parse(useLocation().search);
// Extract Query Params
const {
page,
// sortcolumn, sortorder
sortcolumn,
sortorder,
deleted,
completed
} = search;
const history = useNavigate();
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: {text: ""},
});
const {t} = useTranslation();
const columns = [
{
title: t("tasks.fields.title"),
dataIndex: "title",
key: "title",
sorter: true,
sortOrder: sortcolumn === "title" && sortorder,
},
{
title: t("tasks.fields.description"),
dataIndex: "description",
key: "description",
sorter: true,
sortOrder: sortcolumn === "description" && sortorder,
},
{
title: t("tasks.fields.due_date"),
dataIndex: "due_date",
key: "due_date",
},
{
title: t("tasks.fields.assigned_to"),
dataIndex: "assigned_to",
key: "assigned_to",
},
{
title: t("tasks.fields.completed"),
dataIndex: "completed",
key: "completed",
},
{
title: t("tasks.fields.completed_at"),
dataIndex: "completed_at",
key: "completed_at",
},
{
title: t("tasks.fields.remind_at"),
dataIndex: "remind_at",
key: "remind_at",
sorter: true,
sortOrder: sortcolumn === "due_date" && sortorder,
width: '5%',
render: (text, record) => <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.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: ""},
});
// Columns definition remains the same
const handleTableChange = (pagination, filters, sorter) => {
setState({...state, filteredInfo: filters, sortedInfo: sorter});
search.page = pagination.current;
@@ -76,43 +138,49 @@ export default function TaskListComponent({
search.sortorder = sorter.order;
history({search: queryString.stringify(search)});
};
const handleSwitchChange = (param, value) => {
if (value) {
search[param] = "true";
} else {
delete search[param];
}
history({search: queryString.stringify(search)});
};
/**
* Extra actions for the tasks
* @type {Function}
*/
const tasksExtra = useCallback(() => {
return (
<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.refresh')}
onClick={() => refetch()}>
<SyncOutlined/>
</Button>
</Space>
);
}, [refetch, deleted, completed]);
return (
<Card
title={t("menus.header.tasks")}
extra={
<Space wrap>
{search.search && (
<>
<Typography.Title level={4}>
{t("general.labels.searchresults", {search: search.search})}
</Typography.Title>
<Button
onClick={() => {
delete search.search;
history({search: queryString.stringify(search)});
}}
>
{t("general.actions.clear")}
</Button>
</>
)}
<Button onClick={() => refetch()}>
<SyncOutlined/>
</Button>
<Input.Search
placeholder={search.search || t("general.labels.search")}
onSearch={(value) => {
if (value?.length >= 3) {
search.search = value;
} else {
delete search.search;
}
history({search: queryString.stringify(search)});
}}
enterButton
/>
</Space>
}
extra={tasksExtra()}
>
<Table
loading={loading}