Add task drag-and-drop reordering
Order tasks by priority (ASC) by default. Enable drag-and-drop on the TODO and DONE tabs using native HTML5 drag events, following the compliance external URLs pattern. The "All" tab remains read-only since priority is scoped per state. Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -15,15 +15,13 @@ const tasksQuery = graphql`
|
||||
... on Measure {
|
||||
id
|
||||
canCreateTask: permission(action: "core:task:create")
|
||||
tasks(first: 100)
|
||||
tasks(first: 100, orderBy: { field: PRIORITY, direction: ASC })
|
||||
@connection(key: "Measure__tasks")
|
||||
@required(action: THROW) {
|
||||
__id
|
||||
edges @required(action: THROW) {
|
||||
node {
|
||||
id
|
||||
# eslint-disable-next-line relay/unused-fields
|
||||
state
|
||||
...TasksCard_task
|
||||
...TaskFormDialogFragment
|
||||
...TasksCard_TaskRowFragment
|
||||
}
|
||||
|
||||
@@ -1,83 +1,52 @@
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { Button, IconPlusLarge, PageHeader } from "@probo/ui";
|
||||
import {
|
||||
type PreloadedQuery,
|
||||
usePreloadedQuery,
|
||||
useRefetchableFragment,
|
||||
} from "react-relay";
|
||||
import { type PreloadedQuery, usePreloadedQuery } from "react-relay";
|
||||
import { graphql } from "relay-runtime";
|
||||
|
||||
import type { TaskGraphQuery } from "#/__generated__/core/TaskGraphQuery.graphql";
|
||||
import type { TasksPageFragment$key } from "#/__generated__/core/TasksPageFragment.graphql";
|
||||
import type { TasksPageFragment_query } from "#/__generated__/core/TasksPageFragment_query.graphql";
|
||||
import type { TasksCardOrganizationFragment$key } from "#/__generated__/core/TasksCardOrganizationFragment.graphql";
|
||||
import type { TasksPageQuery } from "#/__generated__/core/TasksPageQuery.graphql";
|
||||
import TaskFormDialog from "#/components/tasks/TaskFormDialog";
|
||||
import { TasksCard } from "#/components/tasks/TasksCard";
|
||||
import { tasksQuery } from "#/hooks/graph/TaskGraph";
|
||||
import { OrganizationTasksCard } from "#/components/tasks/TasksCard";
|
||||
|
||||
const tasksFragment = graphql`
|
||||
fragment TasksPageFragment on Organization
|
||||
@refetchable(queryName: "TasksPageFragment_query")
|
||||
@argumentDefinitions(
|
||||
first: { type: "Int", defaultValue: 500 }
|
||||
order: { type: "TaskOrder", defaultValue: null }
|
||||
after: { type: "CursorKey", defaultValue: null }
|
||||
before: { type: "CursorKey", defaultValue: null }
|
||||
last: { type: "Int", defaultValue: null }
|
||||
) {
|
||||
canCreateTask: permission(action: "core:task:create")
|
||||
tasks(
|
||||
first: $first
|
||||
after: $after
|
||||
last: $last
|
||||
before: $before
|
||||
orderBy: $order
|
||||
) @connection(key: "TasksPageFragment_tasks") @required(action: THROW) {
|
||||
__id
|
||||
edges @required(action: THROW) {
|
||||
# eslint-disable-next-line relay/unused-fields
|
||||
node {
|
||||
# eslint-disable-next-line relay/unused-fields
|
||||
id
|
||||
# eslint-disable-next-line relay/unused-fields
|
||||
state
|
||||
...TaskFormDialogFragment
|
||||
...TasksCard_TaskRowFragment
|
||||
}
|
||||
export const tasksPageQuery = graphql`
|
||||
query TasksPageQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
... on Organization {
|
||||
...TasksCardOrganizationFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
queryRef: PreloadedQuery<TaskGraphQuery>;
|
||||
queryRef: PreloadedQuery<TasksPageQuery>;
|
||||
}
|
||||
|
||||
export default function TasksPage({ queryRef }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const query = usePreloadedQuery(tasksQuery, queryRef);
|
||||
const [data] = useRefetchableFragment<TasksPageFragment_query, TasksPageFragment$key>(
|
||||
tasksFragment,
|
||||
query.organization as TasksPageFragment$key,
|
||||
);
|
||||
const connectionId = data.tasks.__id;
|
||||
const query = usePreloadedQuery(tasksPageQuery, queryRef);
|
||||
usePageTitle(__("Tasks"));
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader
|
||||
title={__("Tasks")}
|
||||
description={__(
|
||||
"Track your assigned compliance tasks and keep progress on track.",
|
||||
<OrganizationTasksCard
|
||||
organizationRef={query.organization as TasksCardOrganizationFragment$key}
|
||||
header={({ connectionId, canCreateTask }) => (
|
||||
<PageHeader
|
||||
title={__("Tasks")}
|
||||
description={__(
|
||||
"Track your assigned compliance tasks and keep progress on track.",
|
||||
)}
|
||||
>
|
||||
{canCreateTask && (
|
||||
<TaskFormDialog connection={connectionId}>
|
||||
<Button icon={IconPlusLarge}>{__("New task")}</Button>
|
||||
</TaskFormDialog>
|
||||
)}
|
||||
</PageHeader>
|
||||
)}
|
||||
>
|
||||
{data.canCreateTask && (
|
||||
<TaskFormDialog connection={connectionId}>
|
||||
<Button icon={IconPlusLarge}>{__("New task")}</Button>
|
||||
</TaskFormDialog>
|
||||
)}
|
||||
</PageHeader>
|
||||
<TasksCard connectionId={connectionId} tasks={data.tasks.edges} />
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user