Обновлены локализации для дней недели и месяцев, добавлены новые строки для выбора даты и существующих уроков. В компоненте формы уроков реализован календарь для выбора даты с учетом существующих лекций.
This commit is contained in:
parent
32aad802b9
commit
8a66b96599
@ -217,5 +217,26 @@
|
||||
"journal.pl.days.morning": "Morning",
|
||||
"journal.pl.days.day": "Day",
|
||||
"journal.pl.days.evening": "Evening",
|
||||
"journal.pl.lesson.form.selectTime": "Select time"
|
||||
"journal.pl.lesson.form.selectTime": "Select time",
|
||||
"journal.pl.lesson.existingLessonHint": "There is already a lesson on this day",
|
||||
"journal.pl.lesson.form.selectDate": "Select date",
|
||||
"journal.pl.days.shortMonday": "Mo",
|
||||
"journal.pl.days.shortTuesday": "Tu",
|
||||
"journal.pl.days.shortWednesday": "We",
|
||||
"journal.pl.days.shortThursday": "Th",
|
||||
"journal.pl.days.shortFriday": "Fr",
|
||||
"journal.pl.days.shortSaturday": "Sa",
|
||||
"journal.pl.days.shortSunday": "Su",
|
||||
"journal.pl.months.january": "January",
|
||||
"journal.pl.months.february": "February",
|
||||
"journal.pl.months.march": "March",
|
||||
"journal.pl.months.april": "April",
|
||||
"journal.pl.months.may": "May",
|
||||
"journal.pl.months.june": "June",
|
||||
"journal.pl.months.july": "July",
|
||||
"journal.pl.months.august": "August",
|
||||
"journal.pl.months.september": "September",
|
||||
"journal.pl.months.october": "October",
|
||||
"journal.pl.months.november": "November",
|
||||
"journal.pl.months.december": "December"
|
||||
}
|
@ -214,5 +214,26 @@
|
||||
"journal.pl.days.morning": "Утро",
|
||||
"journal.pl.days.day": "День",
|
||||
"journal.pl.days.evening": "Вечер",
|
||||
"journal.pl.lesson.form.selectTime": "Выберите время"
|
||||
"journal.pl.lesson.form.selectTime": "Выберите время",
|
||||
"journal.pl.lesson.existingLessonHint": "В этот день уже есть лекция",
|
||||
"journal.pl.lesson.form.selectDate": "Выберите дату",
|
||||
"journal.pl.days.shortMonday": "Пн",
|
||||
"journal.pl.days.shortTuesday": "Вт",
|
||||
"journal.pl.days.shortWednesday": "Ср",
|
||||
"journal.pl.days.shortThursday": "Чт",
|
||||
"journal.pl.days.shortFriday": "Пт",
|
||||
"journal.pl.days.shortSaturday": "Сб",
|
||||
"journal.pl.days.shortSunday": "Вс",
|
||||
"journal.pl.months.january": "Январь",
|
||||
"journal.pl.months.february": "Февраль",
|
||||
"journal.pl.months.march": "Март",
|
||||
"journal.pl.months.april": "Апрель",
|
||||
"journal.pl.months.may": "Май",
|
||||
"journal.pl.months.june": "Июнь",
|
||||
"journal.pl.months.july": "Июль",
|
||||
"journal.pl.months.august": "Август",
|
||||
"journal.pl.months.september": "Сентябрь",
|
||||
"journal.pl.months.october": "Октябрь",
|
||||
"journal.pl.months.november": "Ноябрь",
|
||||
"journal.pl.months.december": "Декабрь"
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useForm, Controller } from 'react-hook-form'
|
||||
import {
|
||||
Box,
|
||||
@ -27,9 +27,11 @@ import {
|
||||
useStyleConfig,
|
||||
Select,
|
||||
Wrap,
|
||||
WrapItem
|
||||
WrapItem,
|
||||
IconButton,
|
||||
Center
|
||||
} from '@chakra-ui/react'
|
||||
import { AddIcon, CheckIcon, WarningIcon, RepeatIcon } from '@chakra-ui/icons'
|
||||
import { AddIcon, CheckIcon, WarningIcon, RepeatIcon, ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaRobot } from 'react-icons/fa'
|
||||
import dayjs from 'dayjs'
|
||||
@ -58,6 +60,7 @@ interface LessonFormProps {
|
||||
onSelectAiSuggestion?: (suggestion: any) => void // Обработчик выбора предложения
|
||||
selectedAiSuggestion?: any // Выбранное предложение
|
||||
onRetryAiGeneration?: () => void // Функция для повторного запуска генерации
|
||||
existingLessons?: Array<{ date: string; name: string }> // Добавляем новый проп
|
||||
}
|
||||
|
||||
export const LessonForm = ({
|
||||
@ -72,7 +75,8 @@ export const LessonForm = ({
|
||||
isLoadingAiSuggestions = false,
|
||||
onSelectAiSuggestion = () => {},
|
||||
selectedAiSuggestion,
|
||||
onRetryAiGeneration = () => {}
|
||||
onRetryAiGeneration = () => {},
|
||||
existingLessons
|
||||
}: LessonFormProps) => {
|
||||
const { t } = useTranslation()
|
||||
const isAiSuggested = lesson && !lesson._id && !lesson.id
|
||||
@ -195,6 +199,197 @@ export const LessonForm = ({
|
||||
return days[date.getDay()];
|
||||
};
|
||||
|
||||
// Добавляем вспомогательные функции для календаря
|
||||
const getDaysInMonth = (year: number, month: number) => {
|
||||
return new Date(year, month + 1, 0).getDate();
|
||||
};
|
||||
|
||||
const getFirstDayOfMonth = (year: number, month: number) => {
|
||||
return new Date(year, month, 1).getDay();
|
||||
};
|
||||
|
||||
const isWeekend = (dayOfWeek: number) => {
|
||||
return dayOfWeek === 0 || dayOfWeek === 6; // Воскресенье или суббота
|
||||
};
|
||||
|
||||
const isSameDay = (date1: Date, date2: Date) => {
|
||||
return date1.getFullYear() === date2.getFullYear() &&
|
||||
date1.getMonth() === date2.getMonth() &&
|
||||
date1.getDate() === date2.getDate();
|
||||
};
|
||||
// Компонент календаря
|
||||
interface CalendarProps {
|
||||
selectedDate: Date;
|
||||
onSelectDate: (date: Date) => void;
|
||||
existingLessons?: string[];
|
||||
}
|
||||
|
||||
const Calendar: React.FC<CalendarProps> = ({ selectedDate, onSelectDate, existingLessons = [] }) => {
|
||||
const { t } = useTranslation();
|
||||
const [viewDate, setViewDate] = useState(new Date());
|
||||
|
||||
// Используем короткие названия дней недели из локализации
|
||||
const weekDays = [
|
||||
t('journal.pl.days.shortMonday'),
|
||||
t('journal.pl.days.shortTuesday'),
|
||||
t('journal.pl.days.shortWednesday'),
|
||||
t('journal.pl.days.shortThursday'),
|
||||
t('journal.pl.days.shortFriday'),
|
||||
t('journal.pl.days.shortSaturday'),
|
||||
t('journal.pl.days.shortSunday'),
|
||||
];
|
||||
|
||||
// Используем локализованные названия месяцев
|
||||
const monthNames = [
|
||||
t('journal.pl.months.january'),
|
||||
t('journal.pl.months.february'),
|
||||
t('journal.pl.months.march'),
|
||||
t('journal.pl.months.april'),
|
||||
t('journal.pl.months.may'),
|
||||
t('journal.pl.months.june'),
|
||||
t('journal.pl.months.july'),
|
||||
t('journal.pl.months.august'),
|
||||
t('journal.pl.months.september'),
|
||||
t('journal.pl.months.october'),
|
||||
t('journal.pl.months.november'),
|
||||
t('journal.pl.months.december'),
|
||||
];
|
||||
|
||||
const daysInMonth = getDaysInMonth(viewDate.getFullYear(), viewDate.getMonth());
|
||||
let firstDay = getFirstDayOfMonth(viewDate.getFullYear(), viewDate.getMonth());
|
||||
firstDay = firstDay === 0 ? 6 : firstDay - 1; // Корректируем для начала недели с понедельника
|
||||
|
||||
const days = Array.from({ length: 42 }, (_, i) => {
|
||||
const dayNumber = i - firstDay + 1;
|
||||
if (dayNumber > 0 && dayNumber <= daysInMonth) {
|
||||
const date = new Date(viewDate.getFullYear(), viewDate.getMonth(), dayNumber);
|
||||
return {
|
||||
date,
|
||||
dayOfMonth: dayNumber,
|
||||
isCurrentMonth: true,
|
||||
isWeekend: isWeekend(date.getDay()),
|
||||
isToday: isSameDay(date, new Date()),
|
||||
isSelected: isSameDay(date, selectedDate)
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// Добавим функцию проверки наличия лекции в определенный день
|
||||
const hasLessonOnDate = (date: Date) => {
|
||||
return existingLessons.some(lessonDate =>
|
||||
isSameDay(new Date(lessonDate), date)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text fontSize="sm" mb={2}>{t('journal.pl.lesson.form.selectDate')}</Text>
|
||||
<HStack justify="space-between" mb={2}>
|
||||
<IconButton
|
||||
aria-label="Previous month"
|
||||
icon={<ChevronLeftIcon />}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
const newDate = new Date(viewDate);
|
||||
newDate.setMonth(newDate.getMonth() - 1);
|
||||
setViewDate(newDate);
|
||||
}}
|
||||
/>
|
||||
<HStack>
|
||||
<Select
|
||||
size="sm"
|
||||
value={viewDate.getMonth()}
|
||||
onChange={(e) => {
|
||||
const newDate = new Date(viewDate);
|
||||
newDate.setMonth(parseInt(e.target.value));
|
||||
setViewDate(newDate);
|
||||
}}
|
||||
>
|
||||
{monthNames.map((month, i) => (
|
||||
<option key={i} value={i}>{month}</option>
|
||||
))}
|
||||
</Select>
|
||||
<Select
|
||||
size="sm"
|
||||
value={viewDate.getFullYear()}
|
||||
onChange={(e) => {
|
||||
const newDate = new Date(viewDate);
|
||||
newDate.setFullYear(parseInt(e.target.value));
|
||||
setViewDate(newDate);
|
||||
}}
|
||||
>
|
||||
{Array.from({ length: 5 }, (_, i) => {
|
||||
const year = new Date().getFullYear() + i;
|
||||
return <option key={year} value={year}>{year}</option>;
|
||||
})}
|
||||
</Select>
|
||||
</HStack>
|
||||
<IconButton
|
||||
aria-label="Next month"
|
||||
icon={<ChevronRightIcon />}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
const newDate = new Date(viewDate);
|
||||
newDate.setMonth(newDate.getMonth() + 1);
|
||||
setViewDate(newDate);
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
|
||||
<SimpleGrid columns={7} spacing={1}>
|
||||
{weekDays.map(day => (
|
||||
<Center key={day} py={1}>
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
{day}
|
||||
</Text>
|
||||
</Center>
|
||||
))}
|
||||
{days.map((day, i) => {
|
||||
const hasLesson = day?.isCurrentMonth && hasLessonOnDate(day.date);
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={i}
|
||||
size="sm"
|
||||
variant={day?.isSelected ? "solid" : "ghost"}
|
||||
colorScheme={day?.isSelected ? "blue" : day?.isWeekend ? "red" : "gray"}
|
||||
opacity={day?.isCurrentMonth ? 1 : 0}
|
||||
onClick={() => day?.date && onSelectDate(day.date)}
|
||||
h="32px"
|
||||
disabled={!day?.isCurrentMonth}
|
||||
position="relative"
|
||||
_after={hasLesson ? {
|
||||
content: '""',
|
||||
position: "absolute",
|
||||
bottom: "2px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
width: "4px",
|
||||
height: "4px",
|
||||
borderRadius: "full",
|
||||
bg: day?.isSelected ? "white" : "blue.500",
|
||||
_dark: {
|
||||
bg: day?.isSelected ? "white" : "blue.300"
|
||||
}
|
||||
} : undefined}
|
||||
title={hasLesson ? t('journal.pl.lesson.existingLessonHint') : undefined}
|
||||
>
|
||||
<Text
|
||||
fontSize="xs"
|
||||
fontWeight={day?.isToday ? "bold" : "normal"}
|
||||
textDecoration={day?.isToday ? "underline" : "none"}
|
||||
>
|
||||
{day?.dayOfMonth}
|
||||
</Text>
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card align="left" bg={isAiSuggested ? aiHighlightColor : undefined}>
|
||||
<CardHeader display="flex">
|
||||
@ -225,57 +420,33 @@ export const LessonForm = ({
|
||||
name="date"
|
||||
rules={{ required: t('journal.pl.common.required') }}
|
||||
render={({ field }) => {
|
||||
// Разделяем текущее значение на дату и время
|
||||
const [currentDate = '', currentTime = '00:00:00'] = field.value.split('T');
|
||||
// Получаем часы и минуты без секунд для сравнения
|
||||
const currentTimeShort = currentTime.split(':').slice(0, 2).join(':');
|
||||
const selectedDate = new Date(currentDate);
|
||||
|
||||
// Получаем существующие лекции из пропсов компонента
|
||||
const existingLessons2 = existingLessons?.map(lesson => lesson.date) || [];
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel>{t('journal.pl.lesson.form.date')}</FormLabel>
|
||||
<VStack align="stretch" spacing={4}>
|
||||
<HStack spacing={2}>
|
||||
{[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 (
|
||||
<Button
|
||||
key={daysToAdd}
|
||||
size="sm"
|
||||
variant={currentDate === formattedDate ? "solid" : "outline"}
|
||||
colorScheme="blue"
|
||||
onClick={() => {
|
||||
// Сохраняем текущее время при смене даты
|
||||
field.onChange(`${formattedDate}T${currentTime}:00`);
|
||||
}}
|
||||
>
|
||||
{daysToAdd === 0 ? t('journal.pl.today') :
|
||||
daysToAdd === 1 ? t('journal.pl.tomorrow') :
|
||||
t('journal.pl.dayAfterTomorrow')}
|
||||
<Text as="span" fontSize="xs" ml={1} color="gray.500">
|
||||
({dayOfWeek})
|
||||
</Text>
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</HStack>
|
||||
|
||||
<Input
|
||||
value={currentDate}
|
||||
onChange={(e) => {
|
||||
// При ручном изменении даты сохраняем текущее время
|
||||
field.onChange(`${e.target.value}T${currentTime}:00`);
|
||||
}}
|
||||
type="date"
|
||||
size="sm"
|
||||
/>
|
||||
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={4}>
|
||||
{/* Календарь */}
|
||||
<Box>
|
||||
<Calendar
|
||||
selectedDate={selectedDate}
|
||||
existingLessons={existingLessons2}
|
||||
onSelectDate={(date) => {
|
||||
const formattedDate = dateToCalendarFormat(date.toISOString()).split('T')[0];
|
||||
field.onChange(`${formattedDate}T${currentTimeShort}:00`);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Временные слоты */}
|
||||
<Box>
|
||||
<Text fontSize="sm" mb={2}>{t('journal.pl.lesson.form.selectTime')}:</Text>
|
||||
<SimpleGrid columns={{ base: 1, md: 3 }} spacing={4}>
|
||||
<SimpleGrid columns={1} spacing={4}>
|
||||
{Object.entries(timeGroups).map(([groupName, slots]) => (
|
||||
<Box key={groupName}>
|
||||
<Text fontSize="xs" color="gray.500" mb={1}>
|
||||
@ -284,7 +455,6 @@ export const LessonForm = ({
|
||||
<Wrap spacing={1}>
|
||||
{slots.map(slot => {
|
||||
const isSelected = currentTimeShort === slot;
|
||||
|
||||
return (
|
||||
<WrapItem key={slot}>
|
||||
<Button
|
||||
@ -307,7 +477,7 @@ export const LessonForm = ({
|
||||
))}
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
</VStack>
|
||||
</SimpleGrid>
|
||||
</FormControl>
|
||||
);
|
||||
}}
|
||||
|
@ -359,6 +359,10 @@ const LessonList = () => {
|
||||
onSelectAiSuggestion={handleSelectAiSuggestion}
|
||||
selectedAiSuggestion={suggestedLessonToCreate}
|
||||
onRetryAiGeneration={handleRetryAiGeneration}
|
||||
existingLessons={data?.body?.map(lesson => ({
|
||||
date: lesson.date,
|
||||
name: lesson.name
|
||||
}))}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
|
@ -35,7 +35,7 @@ function readAndModifyJson(filePath) {
|
||||
jsonContent.body.forEach((lesson) => {
|
||||
// Случайная дата в пределах последних 3 месяцев
|
||||
const randomDate = new Date();
|
||||
randomDate.setMonth(randomDate.getMonth() - Math.random() * 3);
|
||||
randomDate.setDate(randomDate.getDate() - Math.random() * 30);
|
||||
lesson.date = randomDate.toISOString();
|
||||
lesson.created = new Date(randomDate.getTime() - 86400000).toISOString(); // Создан за день до даты
|
||||
});
|
||||
|
@ -590,8 +590,8 @@
|
||||
"sub": "developer",
|
||||
"email": "email@email.ml"
|
||||
},
|
||||
"startDt": "2024-08-25T17:40:17.814Z",
|
||||
"created": "2024-08-25T17:40:17.814Z",
|
||||
"startDt": "2024-08-25T17:30:00.000Z",
|
||||
"created": "2024-08-25T17:40:17.000Z",
|
||||
"examWithJury2": {
|
||||
"_id": "66cf3d3f4637d420d6271451",
|
||||
"name": "Хакатон",
|
||||
|
@ -19,7 +19,7 @@
|
||||
"email": "primakovpro@gmail.com"
|
||||
}
|
||||
],
|
||||
"date": "2024-04-16T13:38:00.000Z",
|
||||
"date": "2024-04-16T13:30:00.000Z",
|
||||
"created": "2024-04-16T13:38:23.381Z",
|
||||
"id": "661e7f4f69f40b0ebebcd5e4"
|
||||
},
|
||||
@ -37,7 +37,7 @@
|
||||
"email": "primakovpro@gmail.com"
|
||||
}
|
||||
],
|
||||
"date": "2024-08-04T07:00:00.000Z",
|
||||
"date": "2024-08-04T08:00:00.000Z",
|
||||
"created": "2024-08-04T06:23:28.491Z",
|
||||
"id": "66af1e60a0eef5a89f99aa94"
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user