Skip to content

Commit

Permalink
Merge pull request #8 from Hurly77/bug-fixes
Browse files Browse the repository at this point in the history
Bug fixes
  • Loading branch information
Hurly77 committed Jun 30, 2024
2 parents 319aeb1 + ef145a3 commit 80be5aa
Show file tree
Hide file tree
Showing 25 changed files with 444 additions and 222 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function DropdownsDueDateDropdown(props: TaskSpecificDropdownsPro
const { placeholder, hasChip, setTask, task, datePickerOpen, setDatePickerOpen } = props;
const { taskEditorOpen } = React.useContext(TasksLayoutContext);

function handleOnClick(option: (typeof DUE_DATE_DROPDOWN_OPTIONS)[0]) {
function handleOnClick(option: { key: string; name: string; value: Date | null }) {
if (task) {
setTask({
...task,
Expand All @@ -31,7 +31,7 @@ export default function DropdownsDueDateDropdown(props: TaskSpecificDropdownsPro
if (taskEditorOpen && task) {
updateTask({
id: task.id,
date: option.value?.toISOString(),
date: option.value?.toISOString() ?? null,
});
}
}
Expand Down Expand Up @@ -59,11 +59,22 @@ export default function DropdownsDueDateDropdown(props: TaskSpecificDropdownsPro
</DropdownItem>
))}
</DropdownSection>
<DropdownSection>
<DropdownSection showDivider={!!task?.date}>
<DropdownItem onClick={() => setDatePickerOpen("due_date")} key="Custom">
Custom
</DropdownItem>
</DropdownSection>
<DropdownSection className={task?.date ? "block" : "hidden"}>
<DropdownItem
color="danger"
variant="solid"
key="No Date"
className="text-danger font-medium"
onClick={() => handleOnClick({ key: "No Date", name: "No Date", value: null })}
>
No Date
</DropdownItem>
</DropdownSection>
</DropdownMenu>
</Dropdown>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ export default function DropdownsReminderDropdown(props: TaskSpecificDropdownsPr
const { placeholder, hasChip, setTask, task, datePickerOpen, setDatePickerOpen } = props;
const { taskEditorOpen } = React.useContext(TasksLayoutContext);

function handleOnClick(option: (typeof REMINDER_DROPDOWN_OPTIONS)[0]) {
function handleOnClick(option: { key: string; name: string; value: Date | null }) {
if (task) setTask({ ...task, reminder: option.value });

if (taskEditorOpen && task) {
updateTask({
id: task.id,
reminder: option.value?.toISOString(),
reminder: option.value?.toISOString() ?? null,
});
}
}
Expand Down Expand Up @@ -62,11 +62,23 @@ export default function DropdownsReminderDropdown(props: TaskSpecificDropdownsPr
</DropdownItem>
))}
</DropdownSection>
<DropdownSection>
<DropdownSection showDivider={!!task?.reminder}>
<DropdownItem onClick={() => setDatePickerOpen("reminder")} key="Custom">
Custom
</DropdownItem>
</DropdownSection>

<DropdownSection className={task?.reminder ? "block" : "hidden"}>
<DropdownItem
key="Remove"
color="danger"
variant="solid"
className="text-danger font-medium"
onClick={() => handleOnClick({ key: "remove", name: "remove", value: null })}
>
Remove Reminder
</DropdownItem>
</DropdownSection>
</DropdownMenu>
</Dropdown>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { TaskFormat } from "@/lib/sdk/models";

import createTaskRepeat from "@/lib/sdk/methods/create-task-repeat";
import updateTaskRepeat from "@/lib/sdk/methods/update-task-repeat";
import { supabase } from "@/lib/sdk/utilities/supabase";

export default function DropdownsRepeatDropdown(props: TaskSpecificDropdownsProps<TaskFormat>) {
const { placeholder, hasChip, setTask, task, datePickerOpen, setDatePickerOpen } = props;
Expand All @@ -42,6 +43,18 @@ export default function DropdownsRepeatDropdown(props: TaskSpecificDropdownsProp
}
}

async function handleRemoveRepeat() {
if (task) {
setTask({
...task,
repeat: null,
});
}
if (taskEditorOpen && task?.repeat_id) {
await supabase.from("task_repeat").delete().eq("id", task?.repeat_id);
}
}

return (
<>
{datePickerOpen !== "repeat" ? (
Expand Down Expand Up @@ -70,11 +83,22 @@ export default function DropdownsRepeatDropdown(props: TaskSpecificDropdownsProp
</DropdownItem>
))}
</DropdownSection>
<DropdownSection>
<DropdownSection showDivider={!!task?.repeat}>
<DropdownItem onClick={() => setDatePickerOpen("repeat")} key="Custom">
Custom
</DropdownItem>
</DropdownSection>
<DropdownSection className={task?.repeat ? "block" : "hidden"}>
<DropdownItem
key="Remove"
color="danger"
variant="solid"
className="text-danger font-medium"
onClick={handleRemoveRepeat}
>
Remove Repeat
</DropdownItem>
</DropdownSection>
</DropdownMenu>
</Dropdown>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ export default function TaskEditor() {
const open = ctx.taskEditorOpen;
const { taskInEdit } = ctx;

console.log(isSmall);

const variantsSmall = {
open: { x: 0 },
closed: { x: "24rem" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { TasksLayoutContext } from "../../context/TasksLayoutContext";
import { updateTask } from "@/lib/sdk/methods";

export default function TaskTitleAnStepInputs() {
const { taskInEdit, setTaskInEdit, taskListState } = React.useContext(TasksLayoutContext);
const [taskList] = taskListState;
const { taskInEdit, setTaskInEdit } = React.useContext(TasksLayoutContext);
const [nextStep, setNextStep] = React.useState({
task_id: taskInEdit?.id,
title: "",
Expand All @@ -28,6 +27,7 @@ export default function TaskTitleAnStepInputs() {
<div className="p-4 w-full dark:bg-default-100 bg-white space-y-1 rounded-sm">
<div className="flex sticky top-0 dark:bg-default-100 bg-white z-10">
<Checkbox
defaultSelected={taskInEdit?.completed}
onValueChange={(isCompleted) =>
updateTask({
id: taskInEdit.id,
Expand Down
32 changes: 23 additions & 9 deletions src/components/layouts/tasks/components/TaskTiles/TaskTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,32 @@ import { Checkbox } from "@nextui-org/react";
import { motion } from "framer-motion";
import React from "react";
import { TasksLayoutContext } from "../../context/TasksLayoutContext";
import { TaskFetcherKeys } from "@/lib/sdk/constants/global-enums.";
import useTaskList from "../../hooks/useTaskList";

export default function TaskTile(props: {
task: TaskFormat;
type: TaskFetcherKeys;
handleOnChange: (id: number, completed: boolean) => void;
}) {
const { task, handleOnChange } = props;
const { task, type, handleOnChange } = props;

const ctx = React.useContext(TasksLayoutContext);
const { mutate } = useTaskList(type);
const [isImportant, setIsImportant] = React.useState(task.important);
const [isComplete, setIsComplete] = React.useState(task.completed);

async function onChangeImportance() {
setIsImportant(!isImportant);
await supabase.from("tasks").update({ important: !isImportant }).match({ id: task.id }).select();
if (type === "important" && !isImportant) {
console.log("removing task");
mutate((prevTasks) => {
const updatedTasks = prevTasks?.filter((t) => t.id !== task.id);
return updatedTasks;
});
}
}

return (
<motion.li
Expand All @@ -28,21 +45,18 @@ export default function TaskTile(props: {
<Checkbox
radius="full"
color="primary"
isSelected={task.completed}
onValueChange={(isSelected) => {
setIsImportant(isSelected);
handleOnChange(task.id, isSelected);
isSelected={isComplete}
onValueChange={(complete) => {
setIsComplete(!isComplete);
handleOnChange(task.id, complete);
}}
/>
<span>{task.title}</span>
</div>
<div className="p-4">
<StarIcon
className={cls("h-5 w-5 cursor-pointer stroke-primary", isImportant ? "fill-primary" : "")}
onClick={async () => {
setIsImportant(!isImportant);
await supabase.from("tasks").update({ important: !isImportant }).match({ id: task.id }).select();
}}
onClick={onChangeImportance}
/>
</div>
</motion.li>
Expand Down
17 changes: 14 additions & 3 deletions src/components/layouts/tasks/components/TaskTiles/TaskTileForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@ import { TasksLayoutContext } from "../../context/TasksLayoutContext";

import "react-datepicker/dist/react-datepicker.css";
import TaskTileFormBottom from "./TaskTileFormBottom";
import { uuidv4 } from "../../helpers/task-helpers";
import { getPlaceholderTask, uuidv4 } from "../../helpers/task-helpers";
import { supabase } from "@/lib/sdk/utilities/supabase";
import taskCreator from "@/lib/sdk/creators/taskCreator";
import { classNames } from "@/components/layouts/app/helpers/twind-helper";
import { TaskFetcherKeys } from "@/lib/sdk/constants/global-enums.";
import useTaskList from "../../hooks/useTaskList";
import { TaskFormat } from "@/lib/sdk/models";

export default function TaskTileForm({ type }: { type?: TaskFetcherKeys }) {
const ctx = React.useContext(TasksLayoutContext);
const { mutate, taskList } = useTaskList(type ?? TaskFetcherKeys.ALL);

const lastId = Math.max(...(taskList?.map((t) => t.id) ?? [0]));

const [taskList, setTaskList] = ctx.taskListState;
const [isFocused, setIsFocused] = ctx.taskFormFocusedState;

const [currentTask, setCurrentTask] = ctx.currentTaskStates;
Expand All @@ -40,7 +44,14 @@ export default function TaskTileForm({ type }: { type?: TaskFetcherKeys }) {

async function handleSubmitTask(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
taskCreator({ task: currentTask, isMyDay: type === "my_day" });
await taskCreator({ task: currentTask, type: type ?? TaskFetcherKeys.ALL });
const optimisticTask: TaskFormat = {
...currentTask,
id: lastId + 1,
...getPlaceholderTask(type ?? TaskFetcherKeys.ALL),
};

mutate((tasks) => [optimisticTask, ...(tasks ?? [])]);

const { repeat, id, ...task } = currentTask;

Expand Down
65 changes: 36 additions & 29 deletions src/components/layouts/tasks/components/TaskTiles/TaskTileList.tsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,72 @@
import React from "react";
import { TasksLayoutContext } from "../../context/TasksLayoutContext";
import { Checkbox } from "@nextui-org/react";
import { motion, AnimatePresence } from "framer-motion";
import { StarIcon } from "@heroicons/react/24/solid";
import { StarIcon as StarIconOutline } from "@heroicons/react/24/outline";
import { classNames } from "@/app/helpers/twind-helper";
import { motion } from "framer-motion";
import { supabase } from "@/lib/sdk/utilities/supabase";
import useTaskList from "../../hooks/useTaskList";
import { TaskFormat, TaskRow } from "@/lib/sdk/models/TaskModel";
import { TaskFetcherKeys } from "@/lib/sdk/constants/global-enums.";
import TaskTile from "./TaskTile";
import { KeyedMutator } from "swr";
import { ChevronDownIcon } from "@heroicons/react/20/solid";
import { classNames } from "@/components/layouts/app/helpers/twind-helper";

type TaskListProps = {
type: TaskFetcherKeys;
tasks: TaskFormat[];
setTasks: React.Dispatch<React.SetStateAction<TaskFormat[]>>;
mutate: KeyedMutator<TaskFormat[]>;
};

function TaskTileList({ tasks, setTasks }: TaskListProps) {
const ctx = React.useContext(TasksLayoutContext);

function TaskTileList({ type, tasks, mutate }: TaskListProps) {
async function handleOnChange(id: number, completed: boolean) {
setTasks((prevTasks) => {
const updatedTasks = prevTasks.map((task) => {
if (task.id === id) {
return { ...task, completed };
}
return task;
});

return updatedTasks;
});

await supabase.from("tasks").update({ completed }).match({ id }).select();
mutate((prevTasks) => {
return prevTasks?.map((task) => (task.id === id ? { ...task, completed } : task));
}, false);
}

return (
<div>
<motion.ul initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} className="space-y-2">
{tasks.map((task) => (
<TaskTile key={task.id} task={task} handleOnChange={handleOnChange} />
<TaskTile key={task.id} task={task} type={type} handleOnChange={handleOnChange} />
))}
</motion.ul>
</div>
);
}

export default function TaskTiles({ type }: { type: TaskFetcherKeys }) {
const { taskInEdit, setTaskInEdit } = React.useContext(TasksLayoutContext);

const { taskList, setTaskList } = useTaskList(type);
const { taskList, mutate } = useTaskList(type);
const [showCompleted, setShowCompleted] = React.useState(true);

const incompleteTasks = taskList?.filter((task) => !task.completed);
const completedTasks = taskList?.filter((task) => task.completed);
if (!taskList) return <div>Loading...</div>;

const variants = {
show: { opacity: 1, height: "auto" },
hide: { opacity: 1, height: 0 },
};

return (
<div>
<TaskTileList tasks={incompleteTasks ?? []} setTasks={setTaskList} />
<div className="overflow-y-auto max-h-[70vh] custom-scrollbar">
<TaskTileList type={type} tasks={incompleteTasks ?? []} mutate={mutate} />
<div className="py-4">
<h1 className="pb-4">Completed</h1>
<TaskTileList tasks={completedTasks} setTasks={setTaskList} />
<h1 onClick={() => setShowCompleted(!showCompleted)} className="pb-4 flex gap-2">
<ChevronDownIcon
className={classNames("h-6 w-6 transition-transform", showCompleted ? "rotate-0" : "rotate-180")}
/>
<span className="font-bold">Completed</span>
<span>{completedTasks.length}</span>
</h1>
<motion.div
initial={showCompleted ? "show" : "hide"}
animate={showCompleted ? "show" : "hide"}
variants={variants}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<TaskTileList type={type} tasks={completedTasks} mutate={mutate} />
</motion.div>
</div>
</div>
);
Expand Down
1 change: 0 additions & 1 deletion src/components/layouts/tasks/constants/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ declare type UseStateProps<T> = [T, React.Dispatch<React.SetStateAction<T>>];

declare type TasksLayoutContextProps = {
sidebarState: UseStateProps<boolean>;
taskListState: UseStateProps<Task[]>;
taskFormFocusedState: UseStateProps<boolean>;
currentTaskStates: UseStateProps<Task>;
};
Expand Down
4 changes: 1 addition & 3 deletions src/components/layouts/tasks/context/TasksLayoutContext.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from "react";
import { TaskFormat, TaskRow } from "@/lib/sdk/models/TaskModel";
import useTaskList from "../hooks/useTaskList";

type UseStateProps<T> = [T, React.Dispatch<React.SetStateAction<T>>];

export type TasksLayoutContextProps = {
sidebarState: UseStateProps<boolean>;
taskListState: UseStateProps<TaskFormat[]>;
taskFormFocusedState: UseStateProps<boolean>;
currentTaskStates: UseStateProps<Task>;
datePickerStates: UseStateProps<string | null>;
Expand All @@ -24,7 +24,6 @@ export const TasksLayoutContext = React.createContext({} as TasksLayoutContextPr

export default function TasksLayoutContextProvider({ children }: TasksLayoutContextProviderProps) {
const [sidebarOpen, setSidebarOpen] = React.useState(true);
const [taskList, setTaskList] = React.useState<TaskFormat[]>([]);
const [taskFormFocused, setTaskFormFocused] = React.useState(true);
const [calendarOpen, setCalendarOpen] = React.useState(null);
const [taskInEdit, setTaskInEdit] = React.useState<TaskFormat | null>(null);
Expand Down Expand Up @@ -56,7 +55,6 @@ export default function TasksLayoutContextProvider({ children }: TasksLayoutCont
taskEditorOpen,
taskInEdit,
setTaskInEdit,
taskListState: [taskList, setTaskList],
sidebarState: [sidebarOpen, setSidebarOpen] as UseStateProps<boolean>,
taskFormFocusedState: [taskFormFocused, setTaskFormFocused] as UseStateProps<boolean>,
currentTaskStates: [currentTask, setCurrentTask] as UseStateProps<Task>,
Expand Down
Loading

0 comments on commit 80be5aa

Please sign in to comment.