From 32aad802b96362d5b8fe81d9009ee8f32dd1fa9c Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Wed, 26 Mar 2025 23:20:25 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D0=B2=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=81=D0=BB=D0=BE?= =?UTF-8?q?=D1=82=D1=8B=20=D0=B8=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=B1=D0=BE=D1=80=D0=B0=20=D0=B4=D0=B0=D1=82=D1=8B=20=D0=B8=20?= =?UTF-8?q?=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D1=83=D1=80=D0=BE=D0=BA=D0=BE=D0=B2.=20=D0=A0=D0=B5=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D1=8B=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=B3?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B2=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D1=81=D0=BB=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=20=D0=B8=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=81=D0=BB=D0=B5=D0=B4=D1=83=D1=8E?= =?UTF-8?q?=D1=89=D0=B5=D0=B3=D0=BE=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8.=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=BB=D0=BE=D0=BA=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D1=85=20=D1=81=D1=82=D1=80=D0=BE=D0=BA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en.json | 9 +- locales/ru.json | 9 +- .../lesson-list/components/lessons-form.tsx | 170 ++++++++++++++++-- 3 files changed, 168 insertions(+), 20 deletions(-) diff --git a/locales/en.json b/locales/en.json index 97c4950..e086347 100644 --- a/locales/en.json +++ b/locales/en.json @@ -210,5 +210,12 @@ "journal.pl.overview.new": "new", "journal.pl.overview.pastLessonsStats": "Statistics of past lessons", "journal.pl.overview.dayOfWeekHelp": "Only statistics for completed lessons are shown", - "journal.pl.overview.attendanceHelp": "Attendance is calculated based on past lessons only" + "journal.pl.overview.attendanceHelp": "Attendance is calculated based on past lessons only", + "journal.pl.today": "Today", + "journal.pl.tomorrow": "Tomorrow", + "journal.pl.dayAfterTomorrow": "Day after tomorrow", + "journal.pl.days.morning": "Morning", + "journal.pl.days.day": "Day", + "journal.pl.days.evening": "Evening", + "journal.pl.lesson.form.selectTime": "Select time" } \ No newline at end of file diff --git a/locales/ru.json b/locales/ru.json index bcb7d40..0e36fd4 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -207,5 +207,12 @@ "journal.pl.overview.new": "новых", "journal.pl.overview.pastLessonsStats": "Статистика проведённых занятий", "journal.pl.overview.dayOfWeekHelp": "Показана статистика только состоявшихся занятий", - "journal.pl.overview.attendanceHelp": "Посещаемость рассчитана только по прошедшим занятиям" + "journal.pl.overview.attendanceHelp": "Посещаемость рассчитана только по прошедшим занятиям", + "journal.pl.today": "Сегодня", + "journal.pl.tomorrow": "Завтра", + "journal.pl.dayAfterTomorrow": "Послезавтра", + "journal.pl.days.morning": "Утро", + "journal.pl.days.day": "День", + "journal.pl.days.evening": "Вечер", + "journal.pl.lesson.form.selectTime": "Выберите время" } \ No newline at end of file diff --git a/src/pages/lesson-list/components/lessons-form.tsx b/src/pages/lesson-list/components/lessons-form.tsx index acdf034..6363a4b 100644 --- a/src/pages/lesson-list/components/lessons-form.tsx +++ b/src/pages/lesson-list/components/lessons-form.tsx @@ -24,7 +24,10 @@ import { SimpleGrid, Skeleton, SkeletonText, - useStyleConfig + useStyleConfig, + Select, + Wrap, + WrapItem } from '@chakra-ui/react' import { AddIcon, CheckIcon, WarningIcon, RepeatIcon } from '@chakra-ui/icons' import { useTranslation } from 'react-i18next' @@ -39,6 +42,7 @@ import { ErrorSpan } from '../style' interface NewLessonForm { name: string date: string + time: string } interface LessonFormProps { @@ -131,6 +135,66 @@ export const LessonForm = ({ onSelectAiSuggestion(suggestion) } + // Добавляем новые вспомогательные функции + const generateTimeSlots = () => { + const slots = []; + for (let hour = 8; hour <= 21; hour++) { + slots.push(`${hour.toString().padStart(2, '0')}:00`); + slots.push(`${hour.toString().padStart(2, '0')}:30`); + } + return slots; + }; + + const getNextTimeSlots = (date: string, count: number = 3) => { + const currentDate = new Date(); + const selectedDate = new Date(date); + const isToday = selectedDate.toDateString() === currentDate.toDateString(); + + if (!isToday) return []; + + const currentMinutes = currentDate.getHours() * 60 + currentDate.getMinutes(); + const slots = generateTimeSlots(); + + return slots + .map(slot => { + const [hours, minutes] = slot.split(':').map(Number); + const slotMinutes = hours * 60 + minutes; + return { slot, minutes: slotMinutes }; + }) + .filter(({ minutes }) => minutes > currentMinutes) + .slice(0, count) + .map(({ slot }) => slot); + }; + + const timeGroups = { + [`${t('journal.pl.days.morning')} (8-12)`]: generateTimeSlots().filter(slot => { + const hour = parseInt(slot.split(':')[0]); + return hour >= 8 && hour < 12; + }), + [`${t('journal.pl.days.day')} (12-17)`]: generateTimeSlots().filter(slot => { + const hour = parseInt(slot.split(':')[0]); + return hour >= 12 && hour < 17; + }), + [`${t('journal.pl.days.evening')} (17-21)`]: generateTimeSlots().filter(slot => { + const hour = parseInt(slot.split(':')[0]); + return hour >= 17 && hour <= 21; + }) + }; + + // Добавляем функцию для получения дня недели + const getDayOfWeek = (date: Date) => { + const days = [ + t('journal.pl.days.sunday'), + t('journal.pl.days.monday'), + t('journal.pl.days.tuesday'), + t('journal.pl.days.wednesday'), + t('journal.pl.days.thursday'), + t('journal.pl.days.friday'), + t('journal.pl.days.saturday') + ]; + return days[date.getDay()]; + }; + return ( @@ -160,23 +224,93 @@ export const LessonForm = ({ control={control} name="date" rules={{ required: t('journal.pl.common.required') }} - render={({ field }) => ( - - {t('journal.pl.lesson.form.date')} - - {errors.date ? ( - {errors.date?.message} - ) : ( - {t('journal.pl.lesson.form.dateTime')} - )} - - )} + render={({ field }) => { + // Разделяем текущее значение на дату и время + const [currentDate = '', currentTime = '00:00:00'] = field.value.split('T'); + // Получаем часы и минуты без секунд для сравнения + const currentTimeShort = currentTime.split(':').slice(0, 2).join(':'); + + return ( + + {t('journal.pl.lesson.form.date')} + + + {[0, 1, 2].map(daysToAdd => { + const date = new Date(); + date.setDate(date.getDate() + daysToAdd); + const formattedDate = dateToCalendarFormat(date.toISOString()).split('T')[0]; + const dayOfWeek = getDayOfWeek(date); + + return ( + + ); + })} + + + { + // При ручном изменении даты сохраняем текущее время + field.onChange(`${e.target.value}T${currentTime}:00`); + }} + type="date" + size="sm" + /> + + + {t('journal.pl.lesson.form.selectTime')}: + + {Object.entries(timeGroups).map(([groupName, slots]) => ( + + + {groupName} + + + {slots.map(slot => { + const isSelected = currentTimeShort === slot; + + return ( + + + + ); + })} + + + ))} + + + + + ); + }} />