feat: new tasks calendar
This commit is contained in:
@@ -17,8 +17,10 @@ import {
|
||||
InboxArrowDownIcon,
|
||||
CalendarDaysIcon,
|
||||
BookOpenIcon,
|
||||
DocumentDuplicateIcon
|
||||
DocumentDuplicateIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import Dialog from "@/components/ui/Dialog";
|
||||
|
||||
const example_tags: { first: string[]; second: string[] } = {
|
||||
first: ["Программирование", "Информатика", "Физика", "Математика"],
|
||||
@@ -40,8 +42,9 @@ const calendarStyles = {
|
||||
yearTitle: "text-gray-700 font-semibold",
|
||||
monthPicker: "grid grid-cols-3 gap-2",
|
||||
yearPicker: "grid grid-cols-2 gap-2",
|
||||
month: "p-2 text-center cursor-pointer rounded-lg hover:bg-[rgba(251,194,199,0.1)] [&.p-highlight]:bg-[rgba(251,194,199,0.2)]",
|
||||
year: "p-2 text-center cursor-pointer rounded-lg hover:bg-[rgba(251,194,199,0.1)] [&.p-highlight]:bg-[rgba(251,194,199,0.2)]"
|
||||
month:
|
||||
"p-2 text-center cursor-pointer rounded-lg hover:bg-[rgba(251,194,199,0.1)] [&.p-highlight]:bg-[rgba(251,194,199,0.2)]",
|
||||
year: "p-2 text-center cursor-pointer rounded-lg hover:bg-[rgba(251,194,199,0.1)] [&.p-highlight]:bg-[rgba(251,194,199,0.2)]",
|
||||
};
|
||||
|
||||
const ProfileCalendar: FunctionComponent = () => {
|
||||
@@ -56,6 +59,8 @@ const ProfileCalendar: FunctionComponent = () => {
|
||||
const [editContent, setEditContent] = useState<ITask | null>(null);
|
||||
const [calendarDate, setCalendarDate] = useState<Nullable<Date>>();
|
||||
const [tags, setTags] = useState<ITags>({ first: "", second: "" });
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
@@ -72,35 +77,31 @@ 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)
|
||||
date: new Date(task.date),
|
||||
}));
|
||||
setTasks(parsedTasks);
|
||||
}
|
||||
|
||||
const handleStorageChange = (e: StorageEvent) => {
|
||||
if (e.key === "tasks" && e.newValue) {
|
||||
const updatedTasks: ITask[] = JSON.parse(e.newValue).map((task: any) => ({
|
||||
...task,
|
||||
date: new Date(task.date)
|
||||
}));
|
||||
setTasks(updatedTasks);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("storage", handleStorageChange);
|
||||
return () => window.removeEventListener("storage", handleStorageChange);
|
||||
}, []);
|
||||
const handleDeleteTask = () => {
|
||||
if (!editContent) return;
|
||||
setTasks(tasks.filter((task) => task.id !== editContent.id));
|
||||
setIsOpen(false);
|
||||
setShowDeleteDialog(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentDate) return;
|
||||
|
||||
const tasksForDate = tasks.filter(task => {
|
||||
const tasksForDate = tasks.filter((task) => {
|
||||
const taskDate = task.date;
|
||||
return taskDate.getDate() === currentDate.getDate() &&
|
||||
return (
|
||||
taskDate.getDate() === currentDate.getDate() &&
|
||||
taskDate.getMonth() === currentDate.getMonth() &&
|
||||
taskDate.getFullYear() === currentDate.getFullYear();
|
||||
taskDate.getFullYear() === currentDate.getFullYear()
|
||||
);
|
||||
});
|
||||
|
||||
setSelectedTasks(tasksForDate);
|
||||
@@ -110,6 +111,9 @@ 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;
|
||||
@@ -120,43 +124,45 @@ const ProfileCalendar: FunctionComponent = () => {
|
||||
}, [tags]);
|
||||
|
||||
const hasTasksOnDate = (date: CalendarDateTemplateEvent) => {
|
||||
return tasks.some(task => {
|
||||
return tasks.some((task) => {
|
||||
const taskDate = task.date;
|
||||
return taskDate.getDate() === date.day &&
|
||||
taskDate.getMonth() === date.month &&
|
||||
taskDate.getFullYear() === date.year;
|
||||
return (
|
||||
taskDate.getDate() === date.day && taskDate.getMonth() === date.month && taskDate.getFullYear() === date.year
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const dateTemplate = (date: CalendarDateTemplateEvent) => {
|
||||
const isHighlighted = hasTasksOnDate(date);
|
||||
const isSelected = currentDate &&
|
||||
const isSelected =
|
||||
currentDate &&
|
||||
currentDate.getDate() === date.day &&
|
||||
currentDate.getMonth() === date.month &&
|
||||
currentDate.getFullYear() === date.year;
|
||||
const isToday = new Date().getDate() === date.day &&
|
||||
const isToday =
|
||||
new Date().getDate() === date.day &&
|
||||
new Date().getMonth() === date.month &&
|
||||
new Date().getFullYear() === date.year;
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
"relative w-full h-20 flex items-start justify-start p-2 cursor-pointer rounded-lg",
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex h-20 w-full cursor-pointer items-start justify-start rounded-lg p-2",
|
||||
"hover:bg-[rgba(251,194,199,0.1)]",
|
||||
{
|
||||
"bg-[rgba(251,194,199,0.4)]": isSelected,
|
||||
"bg-[rgba(251,194,199,0.2)]": isToday && !isSelected,
|
||||
}
|
||||
)}>
|
||||
)}
|
||||
>
|
||||
<span>{date.day}</span>
|
||||
{isHighlighted && <span className="absolute top-2 right-2 w-2 h-2 rounded-full bg-pink-400" />}
|
||||
{isHighlighted && <span className="absolute top-2 right-2 h-2 w-2 rounded-full bg-pink-400" />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const handleTaskCheck = (taskId: string) => {
|
||||
const updatedTasks = tasks.map(task =>
|
||||
task.id === taskId ? { ...task, checked: !task.checked } : task
|
||||
);
|
||||
const updatedTasks = tasks.map((task) => (task.id === taskId ? { ...task, checked: !task.checked } : task));
|
||||
setTasks(updatedTasks);
|
||||
localStorage.setItem("tasks", JSON.stringify(updatedTasks));
|
||||
};
|
||||
@@ -165,7 +171,7 @@ const ProfileCalendar: FunctionComponent = () => {
|
||||
return new Intl.DateTimeFormat("ru-RU", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric"
|
||||
year: "numeric",
|
||||
}).format(date);
|
||||
};
|
||||
|
||||
@@ -191,7 +197,7 @@ const ProfileCalendar: FunctionComponent = () => {
|
||||
|
||||
const pt = {
|
||||
root: { className: calendarStyles.root },
|
||||
input: { root: { className: calendarStyles.input }},
|
||||
input: { root: { className: calendarStyles.input } },
|
||||
panel: { className: calendarStyles.panel },
|
||||
header: { className: calendarStyles.header },
|
||||
title: { className: calendarStyles.title },
|
||||
@@ -205,7 +211,7 @@ const ProfileCalendar: FunctionComponent = () => {
|
||||
monthPicker: { className: calendarStyles.monthPicker },
|
||||
yearPicker: { className: calendarStyles.yearPicker },
|
||||
month: { className: calendarStyles.month },
|
||||
year: { className: calendarStyles.year }
|
||||
year: { className: calendarStyles.year },
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -276,6 +282,7 @@ const ProfileCalendar: FunctionComponent = () => {
|
||||
/>
|
||||
<input type="checkbox" hidden {...register("checked")} />
|
||||
</div>
|
||||
<div class="flex flex-row gap-4">
|
||||
<div
|
||||
className="flex cursor-pointer flex-col items-center gap-3"
|
||||
onClick={() => {
|
||||
@@ -297,6 +304,14 @@ const ProfileCalendar: FunctionComponent = () => {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="flex cursor-pointer flex-col items-center gap-3"
|
||||
onClick={() => setShowDeleteDialog(true)}
|
||||
>
|
||||
<TrashIcon class="size-6" />
|
||||
<p class="text-[0.7rem]">Удалить</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{errors.name && <p class="text-red-500">{errors.name.message}</p>}
|
||||
{errors.description && <p class="text-red-500">{errors.description.message}</p>}
|
||||
@@ -347,23 +362,29 @@ const ProfileCalendar: FunctionComponent = () => {
|
||||
</form>
|
||||
)}
|
||||
</ModalWindow>
|
||||
|
||||
<Dialog
|
||||
isOpen={showDeleteDialog}
|
||||
setIsOpen={setShowDeleteDialog}
|
||||
title="Удаление задачи"
|
||||
content="Вы уверены, что хотите удалить эту задачу?"
|
||||
onConfirm={handleDeleteTask}
|
||||
confirmText="Удалить"
|
||||
cancelText="Отмена"
|
||||
/>
|
||||
<Calendar
|
||||
value={currentDate}
|
||||
onChange={(e) => setCurrentDate(e ? e.value! : new Date())}
|
||||
inline
|
||||
pt={pt}
|
||||
dateTemplate={dateTemplate}
|
||||
className="[&_.p-datepicker-calendar]:border-separate [&_.p-datepicker-calendar]:border-spacing-2 [&_td]:border [&_td]:border-[rgba(251,194,199,0.38)] [&_td]:rounded-lg [&_.p-datepicker]:!border-0"
|
||||
className="[&_.p-datepicker]:!border-0 [&_.p-datepicker-calendar]:border-separate [&_.p-datepicker-calendar]:border-spacing-2 [&_td]:rounded-lg [&_td]:border [&_td]:border-[rgba(251,194,199,0.38)]"
|
||||
/>
|
||||
|
||||
<div class="mt-8 w-full px-4">
|
||||
<h2 class="mb-4 text-2xl font-semibold">
|
||||
Задачи на {formatDate(currentDate)}
|
||||
</h2>
|
||||
<h2 class="mb-4 text-2xl font-semibold">Задачи на {formatDate(currentDate)}</h2>
|
||||
{selectedTasks.length > 0 ? (
|
||||
<div class="flex flex-col gap-4">
|
||||
{selectedTasks.map(task => (
|
||||
{selectedTasks.map((task) => (
|
||||
<Task
|
||||
key={task.id}
|
||||
name={task.name}
|
||||
@@ -379,9 +400,7 @@ const ProfileCalendar: FunctionComponent = () => {
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div class="rounded-lg bg-[rgba(251,194,199,0.2)] p-4 text-center">
|
||||
На этот день задач нет
|
||||
</div>
|
||||
<div class="rounded-lg bg-[rgba(251,194,199,0.2)] p-4 text-center">На этот день задач нет</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user