Добавлены новые временные слоты и улучшена форма выбора даты и времени для уроков. Реализованы функции для генерации временных слотов и получения следующего доступного времени. Обновлены локализации для новых строк.
This commit is contained in:
parent
03a6172d91
commit
32aad802b9
@ -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"
|
||||
}
|
@ -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": "Выберите время"
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user