Добавлены новые временные слоты и улучшена форма выбора даты и времени для уроков. Реализованы функции для генерации временных слотов и получения следующего доступного времени. Обновлены локализации для новых строк.

This commit is contained in:
Primakov Alexandr Alexandrovich 2025-03-26 23:20:25 +03:00
parent 03a6172d91
commit 32aad802b9
3 changed files with 168 additions and 20 deletions

View File

@ -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"
}

View File

@ -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": "Выберите время"
}

View File

@ -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 (
<Card align="left" bg={isAiSuggested ? aiHighlightColor : undefined}>
<CardHeader display="flex">
@ -160,23 +224,93 @@ export const LessonForm = ({
control={control}
name="date"
rules={{ required: t('journal.pl.common.required') }}
render={({ field }) => (
<FormControl>
<FormLabel>{t('journal.pl.lesson.form.date')}</FormLabel>
<Input
{...field}
required={false}
placeholder={t('journal.pl.lesson.form.datePlaceholder')}
size="md"
type="datetime-local"
/>
{errors.date ? (
<FormErrorMessage>{errors.date?.message}</FormErrorMessage>
) : (
<FormHelperText>{t('journal.pl.lesson.form.dateTime')}</FormHelperText>
)}
</FormControl>
)}
render={({ field }) => {
// Разделяем текущее значение на дату и время
const [currentDate = '', currentTime = '00:00:00'] = field.value.split('T');
// Получаем часы и минуты без секунд для сравнения
const currentTimeShort = currentTime.split(':').slice(0, 2).join(':');
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"
/>
<Box>
<Text fontSize="sm" mb={2}>{t('journal.pl.lesson.form.selectTime')}:</Text>
<SimpleGrid columns={{ base: 1, md: 3 }} spacing={4}>
{Object.entries(timeGroups).map(([groupName, slots]) => (
<Box key={groupName}>
<Text fontSize="xs" color="gray.500" mb={1}>
{groupName}
</Text>
<Wrap spacing={1}>
{slots.map(slot => {
const isSelected = currentTimeShort === slot;
return (
<WrapItem key={slot}>
<Button
size="xs"
variant={isSelected ? "solid" : "outline"}
colorScheme="blue"
onClick={() => {
field.onChange(`${currentDate}T${slot}:00`);
}}
h="24px"
minW="54px"
>
{slot}
</Button>
</WrapItem>
);
})}
</Wrap>
</Box>
))}
</SimpleGrid>
</Box>
</VStack>
</FormControl>
);
}}
/>
<Controller