diff --git a/src/pages/profile_calendar.tsx b/src/pages/profile_calendar.tsx index a99663e..bfcbdae 100644 --- a/src/pages/profile_calendar.tsx +++ b/src/pages/profile_calendar.tsx @@ -5,6 +5,7 @@ import Dialog from "@/components/ui/Dialog"; import ModalWindow from "@/components/ui/Modal"; import { withTitle } from "@/constructors/Component"; import { UrlsTitle } from "@/enums/urls"; +import apiClient from "@/services/api"; import { cn } from "@/utils/class-merge"; import { BookOpenIcon, @@ -15,16 +16,19 @@ import { TrashIcon, } from "@heroicons/react/24/outline"; import { FunctionComponent } from "preact"; -import { useEffect, useState } from "preact/hooks"; +import { useEffect, useMemo, useState } from "preact/hooks"; import { Calendar, CalendarDateTemplateEvent } from "primereact/calendar"; import { Nullable } from "primereact/ts-helpers"; -import { useForm } from "react-hook-form"; -import { ITask, ITaskForm } from "./profile_tasks.dto"; - -const example_tags: { first: string[]; second: string[] } = { - first: ["Программирование", "Информатика", "Физика", "Математика"], - second: ["Лабораторная работа", "Практическая работа", "Домашнее задание", "Экзамен"], -}; +import { SubmitHandler, useForm } from "react-hook-form"; +import { + IApiResponse, + ICreateTaskResponse, + IDeleteTaskResponse, + IEditTaskResponse, + ITask, + ITaskDetails, + ITaskForm, +} from "./profile_tasks.dto"; const calendarStyles = { root: "inline-flex w-full relative", @@ -59,6 +63,9 @@ const ProfileCalendar: FunctionComponent = () => { const [calendarDate, setCalendarDate] = useState>(); const [tags, setTags] = useState({ first: "", second: "", overdue: false }); const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [subjectChoices, setSubjectChoices] = useState>({}); + const [taskTypeChoices, setTaskTypeChoices] = useState>({}); + const [isLoading, setIsLoading] = useState(true); const { handleSubmit, @@ -73,21 +80,164 @@ const ProfileCalendar: FunctionComponent = () => { }); useEffect(() => { - const storedTasks = localStorage.getItem("tasks"); - if (storedTasks) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const parsedTasks: ITask[] = JSON.parse(storedTasks).map((task: any) => ({ - ...task, - date: new Date(task.date), - })); - setTasks(parsedTasks); - } + fetchTasks(); }, []); - const handleDeleteTask = () => { + + const fetchTasks = async () => { + try { + setIsLoading(true); + const response = await apiClient("/api/tasks/view_tasks/"); + + setSubjectChoices(response.subject_choices); + setTaskTypeChoices(response.task_type_choices); + + const convertedTasks: ITask[] = response.tasks.map((apiTask) => ({ + id: apiTask.id.toString(), + name: apiTask.title, + checked: apiTask.isCompleted, + date: new Date(apiTask.due_date), + description: apiTask.description, + tags: [apiTask.subject, apiTask.task_type], + new: false, + })); + + setTasks(convertedTasks); + } catch (error) { + console.error("Failed to fetch tasks:", error); + } finally { + setIsLoading(false); + } + }; + + const example_tags = useMemo( + () => ({ + first: Object.keys(subjectChoices), + second: Object.keys(taskTypeChoices), + }), + [subjectChoices, taskTypeChoices] + ); + + const saveTask: SubmitHandler = async (data) => { + if (!calendarDate) { + setError("date", { message: "Выберите дату" }); + return; + } + if ((!editContent?.tags[0] || !editContent.tags[1]) && (!tags.first || !tags.second)) { + setError("tags", { message: "Выберите теги" }); + return; + } + + try { + const selectedSubject = editContent?.tags[0] || tags.first; + const selectedTaskType = editContent?.tags[1] || tags.second; + + // Format date to DD-MM-YYYYTHH:MM + const formattedDate = calendarDate + .toLocaleString("en-GB", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour: "2-digit", + minute: "2-digit", + hour12: false, + }) + .replace(",", "T") + .replace(/\//g, "-") + .replace("T ", "T"); + + const taskData = { + title: data.name, + description: data.description || "", + subject: selectedSubject, + taskType: selectedTaskType, + dateTime_due: formattedDate, + telegram_notifications: false, + }; + + if (!editContent) { + const response = await apiClient("/api/tasks/create_task/", { + method: "POST", + body: JSON.stringify(taskData), + }); + + if (!response.success) { + throw new Error(response.message); + } + } else { + const response = await apiClient(`/api/tasks/edit_task/${editContent.id}/`, { + method: "PUT", + body: JSON.stringify(taskData), + }); + + if (!response.success) { + throw new Error(response.message); + } + } + + await fetchTasks(); + setIsOpen(false); + setTags({ first: "", second: "", overdue: false }); + } catch (error) { + console.error("Failed to save task:", error); + } + }; + + const handleDeleteTask = async () => { if (!editContent) return; - setTasks(tasks.filter((task) => task.id !== editContent.id)); - setIsOpen(false); - setShowDeleteDialog(false); + + try { + const response = await apiClient(`/api/tasks/delete_task/${editContent.id}/`, { + method: "DELETE", + }); + + if (!response.success) { + throw new Error(response.message); + } + + await fetchTasks(); + setIsOpen(false); + setShowDeleteDialog(false); + } catch (error) { + console.error("Failed to delete task:", error); + } + }; + + const handleTaskCheck = async (taskId: string) => { + try { + await apiClient(`/api/tasks/toggle_complete_task/${taskId}/`, { + method: "PATCH", + }); + + setTasks((prevTasks) => + prevTasks.map((task) => (task.id === taskId ? { ...task, checked: !task.checked } : task)) + ); + } catch (error) { + console.error("Failed to mark task:", error); + } + }; + + const handleViewTask = async (taskId: string) => { + try { + const taskDetails = await apiClient(`/api/tasks/view_task/${taskId}/`); + + const task: ITask = { + id: taskId, + name: taskDetails.title, + checked: false, + date: new Date(taskDetails.dateTime_due), + description: taskDetails.description, + tags: [taskDetails.subject, taskDetails.taskType], + new: false, + }; + + setIsOpen(true); + setIsEdit(true); + setEditContent(task); + setCalendarDate(task.date); + setIsEditModal(false); + } catch (error) { + console.error("Failed to fetch task details:", error); + } }; useEffect(() => { @@ -109,9 +259,6 @@ const ProfileCalendar: FunctionComponent = () => { if (editContent) reset({ ...editContent, date: editContent.date.toISOString().slice(0, 16) }); else reset(); }, [editContent]); - useEffect(() => { - localStorage.setItem("tasks", JSON.stringify(tasks)); - }, [tasks]); useEffect(() => { if (!editContent) return; @@ -175,12 +322,6 @@ const ProfileCalendar: FunctionComponent = () => { ); }; - const handleTaskCheck = (taskId: string) => { - const updatedTasks = tasks.map((task) => (task.id === taskId ? { ...task, checked: !task.checked } : task)); - setTasks(updatedTasks); - localStorage.setItem("tasks", JSON.stringify(updatedTasks)); - }; - const formatDate = (date: Date) => { return new Intl.DateTimeFormat("ru-RU", { day: "numeric", @@ -189,26 +330,6 @@ const ProfileCalendar: FunctionComponent = () => { }).format(date); }; - const saveTask = (data: ITaskForm) => { - if (!calendarDate) { - setError("date", { message: "Выберите дату" }); - return; - } - if ((!editContent?.tags[0] || !editContent.tags[1]) && (!tags.first || !tags.second)) { - setError("tags", { message: "Выберите теги" }); - return; - } - const eTask: ITask = { - ...data, - date: calendarDate, - tags: editContent?.tags.length ? editContent.tags : [tags.first, tags.second], - new: true, - }; - 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: "", overdue: false }); - }; - const pt = { root: { className: calendarStyles.root }, input: { root: { className: calendarStyles.input } }, @@ -230,193 +351,206 @@ const ProfileCalendar: FunctionComponent = () => { return (
- { - setTags({ first: "", second: "", overdue: false }); - }} - onChange={setTags} - /> - { - if (isEdit && !isEditModal) setCalendarDate(null); - }} - onChange={(e) => isEditModal && setCalendarDate(e.value)} - value={calendarDate!} - /> - { - setIsEdit(false); - setEditContent(null); - setIsEditModal(false); - setTags({ first: "", second: "", overdue: false }); - setCalendarDate(null); - }} - > - {isEdit && editContent && ( -
{ - e.preventDefault(); - if (isEditModal) handleSubmit(saveTask)(); - else setIsEditModal(!isEditModal); + {isLoading ? ( +
+
Загрузка...
+
+ ) : ( + <> + { + setTags({ first: "", second: "", overdue: false }); + }} + onChange={setTags} + /> + { + if (isEdit && !isEditModal) setCalendarDate(null); + }} + onChange={(e) => isEditModal && setCalendarDate(e.value)} + value={calendarDate!} + /> + { + setIsEdit(false); + setEditContent(null); + setIsEditModal(false); + setTags({ first: "", second: "", overdue: false }); + setCalendarDate(null); }} > -
-
- -