From a53687d0f8db5f52b21e717e6c75a92b7ce3e6e7 Mon Sep 17 00:00:00 2001 From: Sergey Elpashev Date: Tue, 6 May 2025 12:35:35 +0300 Subject: [PATCH] feat: overdue tasks --- src/components/ModalTags.tsx | 1 + src/components/task.module.scss | 2 +- src/components/task.tsx | 10 +++++- src/pages/profile_calendar.tsx | 10 +++--- src/pages/profile_tasks.tsx | 57 +++++++++++++++++++++++++++++---- 5 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/components/ModalTags.tsx b/src/components/ModalTags.tsx index 12b107b..1727ae5 100644 --- a/src/components/ModalTags.tsx +++ b/src/components/ModalTags.tsx @@ -8,6 +8,7 @@ import ModalWindow, { ModalWindowProps } from "./ui/Modal"; export interface ITags { first: string; second: string; + overdue: boolean; } interface ModalTagsProps extends ModalWindowProps { diff --git a/src/components/task.module.scss b/src/components/task.module.scss index 466c38e..a2c918d 100644 --- a/src/components/task.module.scss +++ b/src/components/task.module.scss @@ -1,5 +1,5 @@ @reference "../index.scss"; .task { - @apply flex h-24 w-full cursor-pointer flex-row items-center justify-start gap-4 rounded-[3rem] bg-[rgba(251,194,199,0.53)] px-5 py-6 text-xl shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] transition-transform hover:scale-[1.05] hover:bg-[rgba(251,194,199,0.7)] active:scale-[1.05] md:w-[500px]; + @apply relative flex h-24 w-full cursor-pointer flex-row items-center justify-start gap-4 rounded-[3rem] bg-[rgba(251,194,199,0.53)] px-5 py-6 text-xl shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] transition-transform hover:scale-[1.05] hover:bg-[rgba(251,194,199,0.7)] active:scale-[1.05] md:w-[500px]; } diff --git a/src/components/task.tsx b/src/components/task.tsx index 9e1c25c..c8c5a66 100644 --- a/src/components/task.tsx +++ b/src/components/task.tsx @@ -6,6 +6,7 @@ import classes from "./task.module.scss"; interface TaskProps { name: string; checked?: boolean; + overdue?: boolean; onClick?: () => void; onMarkClick?: MouseEventHandler; } @@ -30,7 +31,13 @@ const markStyle = tv({ }, }); -const Task: FunctionComponent = ({ name, checked = false, onClick = () => {}, onMarkClick = () => {} }) => { +const Task: FunctionComponent = ({ + name, + checked = false, + onClick = () => {}, + onMarkClick = () => {}, + overdue, +}) => { return (
@@ -44,6 +51,7 @@ const Task: FunctionComponent = ({ name, checked = false, onClick = (

{name} + {overdue && Просрочено}
); diff --git a/src/pages/profile_calendar.tsx b/src/pages/profile_calendar.tsx index fb7db0e..a99663e 100644 --- a/src/pages/profile_calendar.tsx +++ b/src/pages/profile_calendar.tsx @@ -57,7 +57,7 @@ const ProfileCalendar: FunctionComponent = () => { const [isEditModal, setIsEditModal] = useState(false); const [editContent, setEditContent] = useState(null); const [calendarDate, setCalendarDate] = useState>(); - const [tags, setTags] = useState({ first: "", second: "" }); + const [tags, setTags] = useState({ first: "", second: "", overdue: false }); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const { @@ -206,7 +206,7 @@ const ProfileCalendar: FunctionComponent = () => { }; setTasks(tasks.map((task) => (task.id === eTask.id ? eTask : task))); localStorage.setItem("tasks", JSON.stringify(tasks.map((task) => (task.id === eTask.id ? eTask : task)))); - setTags({ first: "", second: "" }); + setTags({ first: "", second: "", overdue: false }); }; const pt = { @@ -236,7 +236,7 @@ const ProfileCalendar: FunctionComponent = () => { tagsList={example_tags} value={tags} onClose={() => { - setTags({ first: "", second: "" }); + setTags({ first: "", second: "", overdue: false }); }} onChange={setTags} /> @@ -256,7 +256,7 @@ const ProfileCalendar: FunctionComponent = () => { setIsEdit(false); setEditContent(null); setIsEditModal(false); - setTags({ first: "", second: "" }); + setTags({ first: "", second: "", overdue: false }); setCalendarDate(null); }} > @@ -359,7 +359,7 @@ const ProfileCalendar: FunctionComponent = () => { })} onClick={() => { if (!isEditModal) return; - setTags({ first: editContent.tags[0], second: editContent.tags[1] }); + setTags({ first: editContent.tags[0], second: editContent.tags[1], overdue: false }); setOpenModalTags(true); }} > diff --git a/src/pages/profile_tasks.tsx b/src/pages/profile_tasks.tsx index 903cc2c..ff076a6 100644 --- a/src/pages/profile_tasks.tsx +++ b/src/pages/profile_tasks.tsx @@ -24,6 +24,7 @@ import { } from "@heroicons/react/24/outline"; import { FunctionComponent } from "preact"; import { useEffect, useMemo, useRef, useState } from "preact/hooks"; +import { Checkbox } from "primereact/checkbox"; import { Nullable } from "primereact/ts-helpers"; import { SubmitHandler, useForm } from "react-hook-form"; import { v4 as uuid } from "uuid"; @@ -46,10 +47,10 @@ const ProfileTasks: FunctionComponent = () => { const [isCreating, setIsCreating] = useState(false); // Включено создание задачи const [editContent, setEditContent] = useState(null); // Содержимое редактируемой задачи const [calendarDate, setCalendarDate] = useState>(); // Выбранная в календаре дата - const [tags, setTags] = useState({ first: "", second: "" }); + const [tags, setTags] = useState({ first: "", second: "", overdue: false }); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [searchQuery, setSearchQuery] = useState(""); // Текст поиска - const [filterTags, setFilterTags] = useState({ first: "", second: "" }); + const [filterTags, setFilterTags] = useState({ first: "", second: "", overdue: false }); const [openFirstList, setOpenFirstList] = useState(false); const [openSecondList, setOpenSecondList] = useState(false); const getDate = useMemo(() => { @@ -99,7 +100,7 @@ const ProfileTasks: FunctionComponent = () => { if (isCreating) setTasks([...tasks, eTask]); else setTasks(tasks.map((task) => (task.id === eTask.id ? eTask : task))); if (isCreating) setIsOpen(false); - setTags({ first: "", second: "" }); + setTags({ first: "", second: "", overdue: false }); }; useEffect(() => { if (editContent) reset({ ...editContent, date: editContent.date.toISOString().slice(0, 16) }); @@ -201,6 +202,8 @@ const ProfileTasks: FunctionComponent = () => { ); } + filtered = filtered.filter((task) => (filterTags.overdue ? task.date < new Date() : task.date >= new Date())); + console.log(filtered); return filtered; }, [tasks, searchQuery, filterTags]); @@ -218,7 +221,7 @@ const ProfileTasks: FunctionComponent = () => { tagsList={example_tags} value={tags} onClose={() => { - if (!isCreating) setTags({ first: "", second: "" }); + if (!isCreating) setTags({ first: "", second: "", overdue: false }); }} onChange={setTags} /> @@ -241,7 +244,7 @@ const ProfileTasks: FunctionComponent = () => { setEditContent(null); setIsCreating(false); setIsEditModal(false); - setTags({ first: "", second: "" }); + setTags({ first: "", second: "", overdue: false }); setCalendarDate(null); }} > @@ -344,7 +347,7 @@ const ProfileTasks: FunctionComponent = () => { })} onClick={() => { if (!isEditModal) return; - setTags({ first: editContent.tags[0], second: editContent.tags[1] }); + setTags({ first: editContent.tags[0], second: editContent.tags[1], overdue: false }); setOpenModalTags(true); }} > @@ -429,7 +432,7 @@ const ProfileTasks: FunctionComponent = () => { confirmText="Удалить" cancelText="Отмена" /> - {!searchQuery && !filterTags.first && !filterTags.second ? ( + {!searchQuery && !filterTags.first && !filterTags.second && !filterTags.overdue ? ( filteredTasks.length > 0 ? ( <>
@@ -467,6 +470,7 @@ const ProfileTasks: FunctionComponent = () => { name={task.name} key={task.id} checked={task.checked} + overdue={task.date < new Date()} onClick={() => { setIsOpen(true); setIsEdit(true); @@ -564,6 +568,7 @@ const ProfileTasks: FunctionComponent = () => { name={task.name} key={task.id} checked={task.checked} + overdue={task.date < new Date()} onClick={() => { setIsOpen(true); setIsEdit(true); @@ -645,6 +650,44 @@ const ProfileTasks: FunctionComponent = () => {
Фильтры
+
+ { + setFilterTags({ ...filterTags, overdue: e.target.checked! }); + }} + pt={{ + root: { + className: cn("cursor-pointer inline-flex relative select-none align-bottom", "w-6 h-6"), + }, + input: { + className: cn( + "absolute appearance-none top-0 left-0 size-full p-0 m-0 opacity-0 z-10 outline-none cursor-pointer" + ), + }, + box: ({ props, context }) => ({ + className: cn( + "flex items-center justify-center", + "border-2 w-6 h-6 text-gray-600 rounded-lg transition-colors duration-200", + { + "border-gray-300 bg-white dark:border-blue-900/40 dark:bg-gray-900": !context.checked, + "border-blue-500 bg-blue-500 dark:border-blue-400 dark:bg-blue-400": context.checked, + }, + { + "hover:border-blue-500 dark:hover:border-blue-400 focus:outline-none focus:outline-offset-0 focus:shadow-[0_0_0_0.2rem_rgba(191,219,254,1)] dark:focus:shadow-[inset_0_0_0_0.2rem_rgba(147,197,253,0.5)]": + !props.disabled, + "cursor-default opacity-60": props.disabled, + } + ), + }), + icon: "w-4 h-4 transition-all duration-200 text-white text-base dark:text-gray-900", + }} + > + +