Skip to content

Commit

Permalink
refactor: 🧑‍💻 Move page and status filters to hook (#18)
Browse files Browse the repository at this point in the history
With this approach we can avoid the need for passing down props to child components
  • Loading branch information
lakshmaji committed Jul 7, 2024
1 parent 622ff07 commit 59bc350
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 61 deletions.
8 changes: 3 additions & 5 deletions client-app/src/application/hooks/useCreateTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,20 @@ import { createTasks } from "../use-cases/tasks/createTask";
import { SubmitHandler, useForm } from "react-hook-form";
import { TaskFormInputs } from "../../presentation/features/tasks/components/TaskForm";
import { useState } from "react";
import { useTaskFilters } from "./useTaskFilters";

interface CreateTaskInput {
title: string;
description?: string;
}
export const useCreateTask = (page: number, status?: TaskStatus) => {
export const useCreateTask = () => {
const [open, setOpen] = useState(false);
const { statusFilter: status, page } = useTaskFilters();

const openAddTask = () => {
setOpen(true);
};

const closeAddTask = () => {
setOpen(false);
};

const modalsetOpen = (value: boolean) => {
setOpen(value);
reset();
Expand Down
9 changes: 3 additions & 6 deletions client-app/src/application/hooks/useDeleteTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ import {
useMutation,
useQueryClient,
} from "@tanstack/react-query";
import { TaskStatus } from "../../domain/models/Task";
import { deleteTask } from "../use-cases/tasks/deleteTask";
import { useTaskFilters } from "./useTaskFilters";

export const useDeleteTask = (
id: string,
page: number,
status?: TaskStatus
) => {
export const useDeleteTask = (id: string) => {
const { statusFilter: status, page } = useTaskFilters();
const queryClient = useQueryClient();

const mutation = useMutation<void, DefaultError, string>({
Expand Down
7 changes: 4 additions & 3 deletions client-app/src/application/hooks/useEditTask.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { ITask, TaskStatus, toTaskStatus } from "../../domain/models/Task";
import { ITask, toTaskStatus } from "../../domain/models/Task";
import { useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import { TaskFormInputs } from "../../presentation/features/tasks/components/TaskForm";
import { useUpdateTask } from "./useUpdateTask";
import { useTaskFilters } from "./useTaskFilters";

export const useEditTask = (task: ITask, page: number, status?: TaskStatus) => {
export const useEditTask = (task: ITask) => {
const [open, setOpen] = useState(false);

const { statusFilter: status, page } = useTaskFilters();
const {
register,
handleSubmit,
Expand Down
29 changes: 29 additions & 0 deletions client-app/src/application/hooks/useTaskFilters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { TaskStatus } from "../../domain/models/Task";
import { URLSearchParamsInit, useSearchParams } from "react-router-dom";

export const useTaskFilters = () => {
const [searchParams, setSearchParams] = useSearchParams();

const statusFilter =
(searchParams.get("statusFilter") as TaskStatus) ?? undefined;
const page = parseInt(searchParams.get("page") || "1", 10);

const changeSearchParams = <T extends Record<string, string>>(props: T) => {
setSearchParams({
// persist previous filter
...Object.fromEntries(searchParams.entries()),
...props,
});
};

const resetSearchParams = (props: URLSearchParamsInit) => {
setSearchParams(props);
};

return {
statusFilter,
page,
changeSearchParams,
resetSearchParams,
};
};
24 changes: 17 additions & 7 deletions client-app/src/application/hooks/useTasks.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { TaskStatus, TasksWithMeta } from "../../domain/models/Task";
import { getTasks } from "../use-cases/tasks/getTasks";
import { useState } from "react";
import { useSearchParams } from "react-router-dom";
import { useTaskFilters } from "./useTaskFilters";

export const useTasks = () => {
const [statusFilter, setStatusFilter] = useState<TaskStatus>();
const [page, setPage] = useState(1);
const { statusFilter, page, resetSearchParams, changeSearchParams } =
useTaskFilters();

const { isPending, isError, error, data, isFetching, isPlaceholderData } =
useQuery<TasksWithMeta>({
queryKey: ["tasks", page, statusFilter],
queryFn: () => getTasks(page, statusFilter),
placeholderData: keepPreviousData,
});

const goToPreviousPage = () => setPage((old) => Math.max(old - 1, 0));
const goToPreviousPage = () => {
changeSearchParams({
page: Math.max(page - 1, 0).toString(),
});
};
const goToNextPage = () => {
if (!isPlaceholderData && data?.meta?.has_more) {
setPage((old) => old + 1);
changeSearchParams({
page: (page + 1).toString(),
});
}
};

const updateFilter = (status?: TaskStatus) => {
// whenever status filter change, reset page so that we wont get unintended results
setPage(1);
setStatusFilter(status);
resetSearchParams({
page: "1",
...(status && { statusFilter: status }),
});
};

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { FC } from "react";
import TaskForm from "./TaskForm";
import { useCreateTask } from "../../../../application/hooks/useCreateTask";
import { TaskStatus } from "../../../../domain/models/Task";
import { useMe } from "../../../../application/hooks/useMe";
interface Props {
page: number;
statusFilter?: TaskStatus;
}
const AddTask: FC<Props> = ({ page, statusFilter }) => {

const AddTask = () => {
const {
register,
onClickAddTodo,
Expand All @@ -18,7 +13,7 @@ const AddTask: FC<Props> = ({ page, statusFilter }) => {
isValid,
open,
modalsetOpen,
} = useCreateTask(page, statusFilter);
} = useCreateTask();

const { user } = useMe();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { FC } from "react";
import TaskForm from "./TaskForm";
import { PencilSquareIcon } from "@heroicons/react/24/outline";
import { ITask, TaskStatus } from "../../../../domain/models/Task";
import { ITask } from "../../../../domain/models/Task";
import { useEditTask } from "../../../../application/hooks/useEditTask";

interface Props {
page: number;
statusFilter?: TaskStatus;
task: ITask;
}
const EditTask: FC<Props> = ({ task, page, statusFilter }) => {
const EditTask: FC<Props> = ({ task }) => {
const {
open,
openAddTask,
Expand All @@ -20,7 +18,7 @@ const EditTask: FC<Props> = ({ task, page, statusFilter }) => {
isValid,
isPending,
onClickUpdateTodo,
} = useEditTask(task, page, statusFilter);
} = useEditTask(task);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { FC } from "react";
import { TaskStatus } from "../../../../domain/models/Task";
import { useTaskFilters } from "../../../../application/hooks/useTaskFilters";

const STATUS_FILTERS = [
{
Expand All @@ -20,19 +20,21 @@ const STATUS_FILTERS = [
},
];

interface Props {
statusFilter?: TaskStatus;
updateFilter: (status?: TaskStatus) => void;
}
const TaskFilters: FC<Props> = ({ statusFilter, updateFilter }) => {
const TaskFilters = () => {
const { statusFilter, resetSearchParams } = useTaskFilters();
return (
<section className="container mx-auto py-4">
<div className="flex justify-center gap-5 rounded-md bg-white p-4 transition-all duration-700 dark:bg-slate-800">
{STATUS_FILTERS.map((eachStatus, i) => {
return (
<button
key={i}
onClick={() => updateFilter(eachStatus.value)}
onClick={() =>
resetSearchParams({
page: "1",
...(eachStatus.value && { statusFilter: eachStatus.value }),
})
}
type="button"
className={`font-thin pb-2 ${
statusFilter === eachStatus.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
SubmitHandler,
UseFormHandleSubmit,
UseFormRegister,
UseFormWatch,
} from "react-hook-form";
import { ITask } from "../../../../domain/models/Task";
import clsx from "clsx";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ import { TaskStatusColors } from "./constants";

interface Props {
task: ITask;
page: number;
statusFilter?: TaskStatus;
}

const TaskItem: React.FC<Props> = ({ task, page, statusFilter }) => {
const removeTask = useDeleteTask(task.id, page, statusFilter);
const TaskItem: React.FC<Props> = ({ task }) => {
const removeTask = useDeleteTask(task.id);

const deleteConfirmation = useDeleteConfirmation({
title: `Delete ${task.title}`,
Expand All @@ -29,7 +27,7 @@ const TaskItem: React.FC<Props> = ({ task, page, statusFilter }) => {
data-testid="todo-item"
role="listitem"
>
<TaskStatusAction task={task} page={page} statusFilter={statusFilter} />
<TaskStatusAction task={task} />
<p
className={clsx(
"grow",
Expand Down Expand Up @@ -61,7 +59,7 @@ const TaskItem: React.FC<Props> = ({ task, page, statusFilter }) => {
{task.status_human_readable}
</span>

<EditTask task={task} page={page} statusFilter={statusFilter} />
<EditTask task={task} />
<button onClick={deleteConfirmation.openDialog} data-testid="delete-btn">
<TrashIcon className="size-6 text-gray-500" />
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { ITask, TaskStatus } from "../../../../domain/models/Task";
import { SparklesIcon, CheckIcon, PlayIcon } from "@heroicons/react/24/outline";
import Spinner from "../../../common/Spinner";
import { useUpdateTaskStatus } from "../../../../application/hooks/useUpdateTaskStatus";
import { useTaskFilters } from "../../../../application/hooks/useTaskFilters";

interface Props {
task: ITask;
page: number;
statusFilter?: TaskStatus;
}
const TaskStatusAction: FC<Props> = ({ task, page, statusFilter }) => {
const TaskStatusAction: FC<Props> = ({ task }) => {
const { statusFilter, page } = useTaskFilters();
const { isPending, changeStatusToCompleted, changeStatusToInprogress } =
useUpdateTaskStatus(task, page, statusFilter);

Expand Down
13 changes: 3 additions & 10 deletions client-app/src/presentation/pages/Tasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ const Tasks = () => {
isFetching,
goToNextPage,
goToPreviousPage,
updateFilter,
statusFilter,
page,
prevPageDisabled,
nextPageDisabled,
Expand All @@ -32,20 +30,15 @@ const Tasks = () => {

return (
<>
<AddTask page={page} statusFilter={statusFilter} />
<TaskFilters statusFilter={statusFilter} updateFilter={updateFilter} />
<AddTask />
<TaskFilters />
<div
className="rounded-t-md bg-white transition-all duration-700 dark:bg-slate-800"
data-testid="todo-list"
role="list"
>
{tasks.map((task) => (
<TaskItem
key={task.id}
task={task}
page={page}
statusFilter={statusFilter}
/>
<TaskItem key={task.id} task={task} />
))}
</div>
<Paginate
Expand Down

0 comments on commit 59bc350

Please sign in to comment.