From dca682ac1e02bb6fe9aac7fdece8cfde81f40c8c Mon Sep 17 00:00:00 2001 From: Sergey Elpashev Date: Mon, 12 May 2025 00:02:22 +0300 Subject: [PATCH] feat: tasks priorities --- src/components/ModalTags.tsx | 4 +- src/components/task.module.scss | 2 +- src/components/task.tsx | 14 +++- src/pages/profile_calendar.tsx | 2 + src/pages/profile_tasks.prime.styles.tsx | 86 ++++++++++++++++++++++++ src/pages/profile_tasks.tsx | 78 +++++++++++++++------ 6 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 src/pages/profile_tasks.prime.styles.tsx diff --git a/src/components/ModalTags.tsx b/src/components/ModalTags.tsx index 827e5bf..94614b1 100644 --- a/src/components/ModalTags.tsx +++ b/src/components/ModalTags.tsx @@ -61,14 +61,14 @@ const ModalTags: FunctionComponent = ({ onClick={() => setShowFirstTags(!showFirstTags)} > - {value?.first || "Предмет"} + {tagsList?.first?.find((tag) => tag.id === value?.first)?.name || "Предмет"} {showFirstTags && (
diff --git a/src/components/task.module.scss b/src/components/task.module.scss index a2c918d..8f283b1 100644 --- a/src/components/task.module.scss +++ b/src/components/task.module.scss @@ -1,5 +1,5 @@ @reference "../index.scss"; .task { - @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]; + @apply relative flex h-24 w-full cursor-pointer flex-row items-center justify-start gap-4 rounded-[3rem] px-5 py-6 text-xl shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] transition-transform hover:scale-[1.05] active:scale-[1.05] md:w-[500px]; } diff --git a/src/components/task.tsx b/src/components/task.tsx index be7d54a..7074f7e 100644 --- a/src/components/task.tsx +++ b/src/components/task.tsx @@ -1,3 +1,4 @@ +import { cn } from "@/utils/class-merge"; import { FunctionComponent } from "preact"; import { MouseEventHandler } from "preact/compat"; import { tv } from "tailwind-variants"; @@ -5,6 +6,7 @@ import classes from "./task.module.scss"; interface TaskProps { name: string; + priority?: number; checked?: boolean; overdue?: boolean; onClick?: () => void; @@ -36,11 +38,21 @@ const Task: FunctionComponent = ({ checked = false, onClick = () => {}, onMarkClick = () => {}, + priority = 4, overdue, }) => { return (
-
+
{ diff --git a/src/pages/profile_calendar.tsx b/src/pages/profile_calendar.tsx index ec540f8..dec2498 100644 --- a/src/pages/profile_calendar.tsx +++ b/src/pages/profile_calendar.tsx @@ -560,6 +560,8 @@ const ProfileCalendar: FunctionComponent = () => { key={task.id} name={task.name} checked={task.checked} + priority={task.priority} + overdue={task.date < new Date()} onClick={() => handleViewTask(task.id)} onMarkClick={() => handleTaskCheck(task.id)} /> diff --git a/src/pages/profile_tasks.prime.styles.tsx b/src/pages/profile_tasks.prime.styles.tsx new file mode 100644 index 0000000..2eb0908 --- /dev/null +++ b/src/pages/profile_tasks.prime.styles.tsx @@ -0,0 +1,86 @@ +import { cn } from "@/utils/class-merge"; +import { FlagIcon as FlagIconSolid } from "@heroicons/react/20/solid"; +import { DropdownPassThroughMethodOptions, DropdownPassThroughOptions } from "primereact/dropdown"; + +export const DropdownStyles: DropdownPassThroughOptions = { + root: ({ props }: DropdownPassThroughMethodOptions) => ({ + className: cn( + "cursor-pointer inline-flex relative select-none mb-2", + "bg-white border border-gray-400 transition-colors duration-200 ease-in-out rounded-md", + "w-full md:w-56", + "hover:border-[rgba(251,194,199,0.7)] focus:outline-none focus:outline-offset-0 focus:shadow-[0_0_0_0.2rem_rgba(251,194,199,1)] ", + { "opacity-60 select-none pointer-events-none cursor-default": props.disabled } + ), + }), + input: ({ props }: DropdownPassThroughMethodOptions) => ({ + className: cn( + "cursor-pointer flex flex-auto overflow-hidden overflow-ellipsis whitespace-nowrap relative", + "bg-transparent border-0 text-gray-800", + "p-3 transition duration-200 bg-transparent rounded appearance-none font-sans text-base", + "focus:outline-none focus:shadow-none", + { "pr-7": props.showClear } + ), + }), + trigger: { + className: cn( + "flex items-center justify-center shrink-0", + "bg-transparent text-gray-500 w-12 rounded-tr-lg rounded-br-lg" + ), + }, + wrapper: { + className: cn("max-h-[200px] overflow-auto", "bg-white text-gray-700 border-0 rounded-md shadow-lg", " "), + }, + list: { className: "py-3 list-none m-0" }, + item: ({ context }: DropdownPassThroughMethodOptions) => ({ + className: cn( + "cursor-pointer font-normal overflow-hidden relative whitespace-nowrap", + "m-0 p-3 border-0 transition-shadow duration-200 rounded-none", + "hover:text-gray-700 hover:bg-gray-200", + { + "text-gray-700": !context.focused && !context.selected, + "bg-gray-300 text-gray-700 ": context.focused && !context.selected, + "bg-[rgba(251,194,199,0.7)] text-black ": context.focused && context.selected, + "bg-blue-50 text-gray-700": !context.focused && context.selected, + "opacity-60 select-none pointer-events-none cursor-default": context.disabled, + } + ), + }), + itemGroup: { + className: cn("m-0 p-3 text-gray-800 bg-white font-bold", " ", "cursor-auto"), + }, + header: { + className: cn("p-3 border-b border-gray-300 text-gray-700 bg-gray-100 mt-0 rounded-tl-lg rounded-tr-lg"), + }, + filterContainer: { + className: "relative", + }, + filterInput: { + className: cn( + "pr-7 -mr-7", + "w-full", + "font-sans text-base text-gray-700 bg-white py-3 px-3 border border-gray-300 transition duration-200 rounded-lg appearance-none", + "hover:border-[rgba(251,194,199,0.7)] focus:outline-none focus:outline-offset-0 focus:shadow-[0_0_0_0.2rem_rgba(251,194,199,1)] " + ), + }, + filterIcon: { className: "-mt-2 absolute top-1/2" }, + clearIcon: { className: "text-gray-500 right-12 -mt-2 absolute top-1/2" }, +}; + +export const selectedPriorityTemplate = (option: { label: string; value: number }) => { + return ( +
+ + {option.label} +
+ ); +}; diff --git a/src/pages/profile_tasks.tsx b/src/pages/profile_tasks.tsx index 6831aa2..ff16046 100644 --- a/src/pages/profile_tasks.tsx +++ b/src/pages/profile_tasks.tsx @@ -28,6 +28,8 @@ import { FunctionComponent } from "preact"; import "preact/debug"; import { useEffect, useMemo, useRef, useState } from "preact/hooks"; import { Checkbox, CheckboxPassThroughMethodOptions } from "primereact/checkbox"; +import { Dropdown } from "primereact/dropdown"; +import { SelectItem } from "primereact/selectitem"; import { Nullable } from "primereact/ts-helpers"; import { SubmitHandler, useForm } from "react-hook-form"; import { v4 as uuid } from "uuid"; @@ -43,6 +45,14 @@ import { IViewTagsResponse, } from "./profile_tasks.dto"; import classes from "./profile_tasks.module.scss"; +import { DropdownStyles, selectedPriorityTemplate } from "./profile_tasks.prime.styles"; + +const priorities: SelectItem[] = [ + { label: "Приоритет 1", value: 1 }, + { label: "Приоритет 2", value: 2 }, + { label: "Приоритет 3", value: 3 }, + { label: "Приоритет 4", value: 4 }, +]; const ProfileTasks: FunctionComponent = () => { const [openModal, setIsOpen] = useState(false); // Открыта модалка @@ -61,6 +71,7 @@ const ProfileTasks: FunctionComponent = () => { const [filterTags, setFilterTags] = useState({ first: 0, second: 0, overdue: false }); const [openFirstList, setOpenFirstList] = useState(false); const [openSecondList, setOpenSecondList] = useState(false); + const [selectedPriority, setSelectedPriority] = useState(4); const getDate = useMemo(() => { const date = new Date(); const formatter = new Intl.DateTimeFormat("ru-RU", { month: "long", day: "numeric" }); @@ -168,9 +179,10 @@ const ProfileTasks: FunctionComponent = () => { const taskData = { title: data.name, description: data.description || "", - subject: selectedSubject, - taskType: selectedTaskType, + subject_id: selectedSubject, + taskType_id: selectedTaskType, dateTime_due: formattedDate, + priority: selectedPriority, telegram_notifications: false, }; @@ -223,6 +235,7 @@ const ProfileTasks: FunctionComponent = () => { priority: 4, checked: false, }); + setSelectedPriority(4); }, [isCreating]); useEffect(() => { if (!editContent) return; @@ -368,6 +381,7 @@ const ProfileTasks: FunctionComponent = () => { setEditContent(task); setCalendarDate(task.date); setIsEditModal(false); + setSelectedPriority(task.priority); } catch (error) { console.error("Failed to fetch task details:", error); } @@ -447,6 +461,7 @@ const ProfileTasks: FunctionComponent = () => { maxLength: { value: 200, message: "Максимум 200 символов в описании" }, })} /> + { {editContent.taskType.name}

+ setSelectedPriority(e.value)} + itemTemplate={selectedPriorityTemplate} + valueTemplate={selectedPriorityTemplate} + />
)} @@ -562,6 +586,14 @@ const ProfileTasks: FunctionComponent = () => { hidden {...register("date")} /> + setSelectedPriority(e.value)} + itemTemplate={selectedPriorityTemplate} + valueTemplate={selectedPriorityTemplate} + />
@@ -640,6 +672,7 @@ const ProfileTasks: FunctionComponent = () => { handleViewTask(task.id)} @@ -656,6 +689,7 @@ const ProfileTasks: FunctionComponent = () => { {groupTasksByDate.tomorrow.map((task) => ( handleViewTask(task.id)} @@ -670,6 +704,7 @@ const ProfileTasks: FunctionComponent = () => { {group.tasks.map((task) => ( handleViewTask(task.id)} @@ -719,6 +754,7 @@ const ProfileTasks: FunctionComponent = () => { filteredTasks.map((task) => ( { >
- {openSearchModal && (