diff --git a/client/src/components/task-list/task-list.component.jsx b/client/src/components/task-list/task-list.component.jsx
index bb56539a9..4a9aea817 100644
--- a/client/src/components/task-list/task-list.component.jsx
+++ b/client/src/components/task-list/task-list.component.jsx
@@ -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
{dueDateFormatted}
;
+ } else {
+ return {dueDateFormatted}
;
+ }
+ } else {
+ return N/A
;
+ }
+}
+const PriorityLabel = ({priority}) => {
+ switch(priority) {
+ case 1:
+ return
+ High
+
;
+ case 2:
+ return
+ Medium
+
;
+ case 3:
+ return
+ Low
+
;
+ default:
+ return
+ None
+
;
+ }
+}
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) => ,
},
{
title: t("tasks.fields.priority"),
dataIndex: "priority",
key: "priority",
+ sorter: true,
+ sortOrder: sortcolumn === "priority" && sortorder,
+ width: '5%',
+ render: (text, record) =>
+ },
+ {
+ title: t("tasks.fields.actions"),
+ key: "toggleCompleted",
+ width: '5%',
+ render: (text, record) => (
+
+
+
+
+ ),
},
];
+ 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 (
+
+ }
+ unCheckedChildren={}
+ title={t('tasks.titles.completed')}
+ checked={completed === "true"}
+ onChange={(value) => handleSwitchChange('completed', value)}
+ />
+ }
+ unCheckedChildren={}
+ title={t('tasks.titles.deleted')}
+ checked={deleted === "true"}
+ onChange={(value) => handleSwitchChange('deleted', value)}
+ />
+
+
+ );
+ }, [refetch, deleted, completed]);
+
return (
- {search.search && (
- <>
-
- {t("general.labels.searchresults", {search: search.search})}
-
-
- >
- )}
-
- {
- if (value?.length >= 3) {
- search.search = value;
- } else {
- delete search.search;
- }
- history({search: queryString.stringify(search)});
- }}
- enterButton
- />
-
- }
+ extra={tasksExtra()}
>
{
+ const completed_at = !currentStatus ? new Date().toISOString() : null;
+ await toggleTaskCompleted({
+ variables: {
+ id: id,
+ completed: !currentStatus,
+ completed_at: completed_at
+ }
+ });
+ refetch();
+ };
+
+ const [toggleTaskDeleted] = useMutation(MUTATION_TOGGLE_TASK_DELETED);
+
+ const toggleDeletedStatus = async (id, currentStatus) => {
+ const deleted_at = !currentStatus ? new Date().toISOString() : null;
+ await toggleTaskDeleted({
+ variables: {
+ id: id,
+ deleted: !currentStatus,
+ deleted_at: deleted_at
+ }
+ });
+ refetch();
+ };
if (error) return ;
@@ -39,6 +74,8 @@ export default function TaskListContainer() {
tasks={data ? data.tasks : null}
total={data ? data.tasks_aggregate.aggregate.count : 0}
refetch={refetch}
+ toggleCompletedStatus={toggleCompletedStatus}
+ toggleDeletedStatus={toggleDeletedStatus}
/>
);
}
diff --git a/client/src/graphql/tasks.queries.js b/client/src/graphql/tasks.queries.js
index 5ddaef3c1..0a4375b1d 100644
--- a/client/src/graphql/tasks.queries.js
+++ b/client/src/graphql/tasks.queries.js
@@ -1,5 +1,9 @@
import {gql} from "@apollo/client";
+/**
+ * All tasks paginated query
+ * @type {DocumentNode}
+ */
export const QUERY_ALL_TASKS_PAGINATED = gql`
query QUERY_ALL_TASKS_PAGINATED(
$offset: Int
@@ -30,10 +34,98 @@ export const QUERY_ALL_TASKS_PAGINATED = gql`
partsorderid
billid
}
- tasks_aggregate(args: {}) {
+ tasks_aggregate {
aggregate {
- count(distinct: true)
+ count
}
}
}
`;
+
+/**
+ * My tasks paginated query
+ * @type {DocumentNode}
+ */
+export const QUERY_MY_TASKS_PAGINATED = gql`
+ query QUERY_TASKS_PAGINATED(
+ $offset: Int
+ $limit: Int
+ $user: String!
+ $bodyshop: uuid!
+ $deleted: Boolean
+ $completed: Boolean
+ $order: [tasks_order_by!]!
+ ) {
+ tasks(
+ offset: $offset
+ limit: $limit
+ order_by: $order
+ where: {
+ assigned_to: {_eq: $user},
+ bodyshopid: {_eq: $bodyshop},
+ deleted: {_eq: $deleted},
+ completed: {_eq: $completed}
+ }
+ ) {
+ id
+ created_at
+ updated_at
+ title
+ description
+ deleted
+ deleted_at
+ due_date
+ created_by
+ assigned_to
+ completed
+ completed_at
+ remind_at
+ priority
+ jobid
+ joblineid
+ partsorderid
+ billid
+ bodyshopid
+ }
+ tasks_aggregate(
+ where: {
+ assigned_to: {_eq: $user},
+ bodyshopid: {_eq: $bodyshop},
+ deleted: {_eq: $deleted},
+ completed: {_eq: $completed}
+ }
+ ) {
+ aggregate {
+ count
+ }
+ }
+ }
+`;
+
+/**
+ * Toggle task completed mutation
+ * @type {DocumentNode}
+ */
+export const MUTATION_TOGGLE_TASK_COMPLETED = gql`
+ mutation MUTATION_TOGGLE_TASK_COMPLETED($id: uuid!, $completed: Boolean!, $completed_at: timestamptz) {
+ update_tasks_by_pk(pk_columns: {id: $id}, _set: {completed: $completed, completed_at: $completed_at}) {
+ id
+ completed
+ completed_at
+ }
+ }
+`;
+
+/**
+ * Toggle task deleted mutation
+ * @type {DocumentNode}
+ */
+export const MUTATION_TOGGLE_TASK_DELETED = gql`
+ mutation MUTATION_TOGGLE_TASK_DELETED($id: uuid!, $deleted: Boolean!, $deleted_at: timestamptz) {
+ update_tasks_by_pk(pk_columns: {id: $id}, _set: {deleted: $deleted, deleted_at: $deleted_at}) {
+ id
+ deleted
+ deleted_at
+ }
+ }
+`;
diff --git a/client/src/pages/tasks/tasks.page.component.jsx b/client/src/pages/tasks/tasks.page.component.jsx
index d1b80c856..253bc9c4c 100644
--- a/client/src/pages/tasks/tasks.page.component.jsx
+++ b/client/src/pages/tasks/tasks.page.component.jsx
@@ -5,7 +5,7 @@ export default function TasksPageComponent({bodyshop, currentUser}) {
return (
-
+
);
}
diff --git a/hasura/metadata/actions.yaml b/hasura/metadata/actions.yaml
index f5639566d..1edb4c2ff 100644
--- a/hasura/metadata/actions.yaml
+++ b/hasura/metadata/actions.yaml
@@ -1,6 +1,6 @@
-actions: [ ]
+actions: []
custom_types:
- enums: [ ]
- input_objects: [ ]
- objects: [ ]
- scalars: [ ]
+ enums: []
+ input_objects: []
+ objects: []
+ scalars: []
diff --git a/hasura/metadata/allow_list.yaml b/hasura/metadata/allow_list.yaml
index 1e3ec7217..fe51488c7 100644
--- a/hasura/metadata/allow_list.yaml
+++ b/hasura/metadata/allow_list.yaml
@@ -1 +1 @@
-[ ]
+[]
diff --git a/hasura/metadata/cron_triggers.yaml b/hasura/metadata/cron_triggers.yaml
index 1e3ec7217..fe51488c7 100644
--- a/hasura/metadata/cron_triggers.yaml
+++ b/hasura/metadata/cron_triggers.yaml
@@ -1 +1 @@
-[ ]
+[]
diff --git a/hasura/metadata/network.yaml b/hasura/metadata/network.yaml
index ffcd4415b..0967ef424 100644
--- a/hasura/metadata/network.yaml
+++ b/hasura/metadata/network.yaml
@@ -1 +1 @@
-{ }
+{}
diff --git a/hasura/metadata/query_collections.yaml b/hasura/metadata/query_collections.yaml
index 1e3ec7217..fe51488c7 100644
--- a/hasura/metadata/query_collections.yaml
+++ b/hasura/metadata/query_collections.yaml
@@ -1 +1 @@
-[ ]
+[]
diff --git a/hasura/metadata/remote_schemas.yaml b/hasura/metadata/remote_schemas.yaml
index 1e3ec7217..fe51488c7 100644
--- a/hasura/metadata/remote_schemas.yaml
+++ b/hasura/metadata/remote_schemas.yaml
@@ -1 +1 @@
-[ ]
+[]
diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml
index 9cc14dd54..38dff43fa 100644
--- a/hasura/metadata/tables.yaml
+++ b/hasura/metadata/tables.yaml
@@ -53,7 +53,7 @@
update_permissions:
- role: user
permission:
- columns: [ ]
+ columns: []
filter:
jobline:
job:
@@ -569,6 +569,13 @@
table:
name: parts_orders
schema: public
+ - name: tasks
+ using:
+ foreign_key_constraint_on:
+ column: billid
+ table:
+ name: tasks
+ schema: public
insert_permissions:
- role: user
permission:
@@ -698,7 +705,7 @@
value_from_env: EVENT_SECRET
request_transform:
method: POST
- query_params: { }
+ query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2
@@ -818,6 +825,13 @@
table:
name: inventory
schema: public
+ - name: ioevents
+ using:
+ foreign_key_constraint_on:
+ column: bodyshopid
+ table:
+ name: ioevents
+ schema: public
- name: jobs
using:
foreign_key_constraint_on:
@@ -846,6 +860,13 @@
table:
name: phonebook
schema: public
+ - name: tasks
+ using:
+ foreign_key_constraint_on:
+ column: bodyshopid
+ table:
+ name: tasks
+ schema: public
- name: timetickets
using:
foreign_key_constraint_on:
@@ -1653,7 +1674,7 @@
columns:
- config
- id
- filter: { }
+ filter: {}
limit: 1
- role: user
permission:
@@ -2491,7 +2512,7 @@
- effective_date
- end_date
- content
- filter: { }
+ filter: {}
- table:
name: exportlog
schema: public
@@ -2675,6 +2696,13 @@
- table:
name: ioevents
schema: public
+ object_relationships:
+ - name: bodyshop
+ using:
+ foreign_key_constraint_on: bodyshopid
+ - name: user
+ using:
+ foreign_key_constraint_on: useremail
- table:
name: job_ar_schema
schema: public
@@ -2824,6 +2852,13 @@
table:
name: parts_order_lines
schema: public
+ - name: tasks
+ using:
+ foreign_key_constraint_on:
+ column: joblineid
+ table:
+ name: tasks
+ schema: public
insert_permissions:
- role: user
permission:
@@ -3311,6 +3346,13 @@
table:
name: scoreboard
schema: public
+ - name: tasks
+ using:
+ foreign_key_constraint_on:
+ column: jobid
+ table:
+ name: tasks
+ schema: public
- name: timetickets
using:
foreign_key_constraint_on:
@@ -4200,13 +4242,13 @@
interval_sec: 10
num_retries: 0
timeout_sec: 60
- webhook: https://worktest.home.irony.online
+ webhook_from_env: HASURA_API_URL
headers:
- name: event-secret
value_from_env: EVENT_SECRET
request_transform:
method: POST
- query_params: { }
+ query_params: {}
template_engine: Kriti
url: '{{$base_url}}/job/statustransition'
version: 2
@@ -4220,7 +4262,7 @@
webhook_from_env: HASURA_API_URL
request_transform:
method: POST
- query_params: { }
+ query_params: {}
template_engine: Kriti
url: '{{$base_url}}/record-handler/arms'
version: 2
@@ -4243,7 +4285,7 @@
value_from_env: EVENT_SECRET
request_transform:
method: POST
- query_params: { }
+ query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2
@@ -4256,13 +4298,13 @@
columns:
- key
- value
- filter: { }
+ filter: {}
- role: user
permission:
columns:
- key
- value
- filter: { }
+ filter: {}
- table:
name: messages
schema: public
@@ -4694,7 +4736,7 @@
value_from_env: EVENT_SECRET
request_transform:
method: POST
- query_params: { }
+ query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2
@@ -5008,6 +5050,13 @@
table:
name: parts_order_lines
schema: public
+ - name: tasks
+ using:
+ foreign_key_constraint_on:
+ column: partsorderid
+ table:
+ name: tasks
+ schema: public
insert_permissions:
- role: user
permission:
@@ -5302,7 +5351,7 @@
value_from_env: EVENT_SECRET
request_transform:
method: POST
- query_params: { }
+ query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2
@@ -5623,6 +5672,129 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
+- table:
+ name: tasks
+ schema: public
+ object_relationships:
+ - name: bill
+ using:
+ foreign_key_constraint_on: billid
+ - name: bodyshop
+ using:
+ foreign_key_constraint_on: bodyshopid
+ - name: job
+ using:
+ foreign_key_constraint_on: jobid
+ - name: jobline
+ using:
+ foreign_key_constraint_on: joblineid
+ - name: parts_order
+ using:
+ foreign_key_constraint_on: partsorderid
+ - name: user
+ using:
+ foreign_key_constraint_on: assigned_to
+ - name: userByCreatedBy
+ using:
+ foreign_key_constraint_on: created_by
+ insert_permissions:
+ - role: user
+ permission:
+ check:
+ bodyshop:
+ associations:
+ _and:
+ - user:
+ authid:
+ _eq: X-Hasura-User-Id
+ - active:
+ _eq: true
+ columns:
+ - completed
+ - deleted
+ - priority
+ - assigned_to
+ - created_by
+ - description
+ - title
+ - completed_at
+ - created_at
+ - deleted_at
+ - due_date
+ - remind_at
+ - updated_at
+ - billid
+ - bodyshopid
+ - id
+ - jobid
+ - joblineid
+ - partsorderid
+ select_permissions:
+ - role: user
+ permission:
+ columns:
+ - completed
+ - deleted
+ - priority
+ - assigned_to
+ - created_by
+ - description
+ - title
+ - completed_at
+ - created_at
+ - deleted_at
+ - due_date
+ - remind_at
+ - updated_at
+ - billid
+ - bodyshopid
+ - id
+ - jobid
+ - joblineid
+ - partsorderid
+ filter:
+ bodyshop:
+ associations:
+ _and:
+ - user:
+ authid:
+ _eq: X-Hasura-User-Id
+ - active:
+ _eq: true
+ allow_aggregations: true
+ update_permissions:
+ - role: user
+ permission:
+ columns:
+ - completed
+ - deleted
+ - priority
+ - assigned_to
+ - created_by
+ - description
+ - title
+ - completed_at
+ - created_at
+ - deleted_at
+ - due_date
+ - remind_at
+ - updated_at
+ - billid
+ - bodyshopid
+ - id
+ - jobid
+ - joblineid
+ - partsorderid
+ filter:
+ bodyshop:
+ associations:
+ _and:
+ - user:
+ authid:
+ _eq: X-Hasura-User-Id
+ - active:
+ _eq: true
+ check: null
- table:
name: timetickets
schema: public
@@ -5843,7 +6015,7 @@
- user:
authid:
_eq: X-Hasura-User-Id
- check: { }
+ check: {}
- table:
name: tt_approval_queue
schema: public
@@ -6006,6 +6178,13 @@
table:
name: exportlog
schema: public
+ - name: ioevents
+ using:
+ foreign_key_constraint_on:
+ column: useremail
+ table:
+ name: ioevents
+ schema: public
- name: messages
using:
foreign_key_constraint_on:
@@ -6034,6 +6213,20 @@
table:
name: parts_orders
schema: public
+ - name: tasks
+ using:
+ foreign_key_constraint_on:
+ column: assigned_to
+ table:
+ name: tasks
+ schema: public
+ - name: tasksByCreatedBy
+ using:
+ foreign_key_constraint_on:
+ column: created_by
+ table:
+ name: tasks
+ schema: public
- name: timetickets
using:
foreign_key_constraint_on:
@@ -6051,7 +6244,7 @@
insert_permissions:
- role: user
permission:
- check: { }
+ check: {}
columns:
- authid
- email
@@ -6253,7 +6446,7 @@
value_from_env: EVENT_SECRET
request_transform:
method: POST
- query_params: { }
+ query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2