2026-09-02 23:47:00 +03:00
|
|
|
|
package agzam.vyatsu.ui.schedule
|
|
|
|
|
|
|
|
|
|
|
|
import agzam.vyatsu.domain.model.Day
|
2026-09-05 00:21:39 +03:00
|
|
|
|
import agzam.vyatsu.domain.model.LessonType
|
2026-09-03 20:53:17 +03:00
|
|
|
|
import agzam.vyatsu.domain.model.SchedulePeriod
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import agzam.vyatsu.domain.model.TimeSlot
|
2026-09-10 00:23:28 +03:00
|
|
|
|
import android.widget.Toast
|
|
|
|
|
|
import androidx.compose.foundation.background
|
2026-09-03 23:34:45 +03:00
|
|
|
|
import androidx.compose.foundation.clickable
|
2026-09-10 00:23:28 +03:00
|
|
|
|
import androidx.compose.foundation.combinedClickable
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.foundation.layout.Arrangement
|
2026-09-04 01:07:09 +03:00
|
|
|
|
import androidx.compose.foundation.layout.Box
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.foundation.layout.Column
|
2026-09-10 00:23:28 +03:00
|
|
|
|
import androidx.compose.foundation.layout.PaddingValues
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.foundation.layout.Row
|
|
|
|
|
|
import androidx.compose.foundation.layout.WindowInsets
|
|
|
|
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
|
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
|
|
|
import androidx.compose.foundation.layout.only
|
|
|
|
|
|
import androidx.compose.foundation.layout.padding
|
2026-09-10 00:23:28 +03:00
|
|
|
|
import androidx.compose.foundation.layout.statusBars
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.foundation.layout.systemBars
|
2026-09-09 22:25:55 +03:00
|
|
|
|
import androidx.compose.foundation.layout.width
|
2026-09-04 01:07:09 +03:00
|
|
|
|
import androidx.compose.foundation.layout.widthIn
|
2026-09-10 00:23:28 +03:00
|
|
|
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
|
|
|
|
import androidx.compose.foundation.lazy.LazyRow
|
2026-09-03 20:53:17 +03:00
|
|
|
|
import androidx.compose.foundation.lazy.items
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.foundation.lazy.itemsIndexed
|
|
|
|
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
2026-09-10 00:23:28 +03:00
|
|
|
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.material.icons.Icons
|
2026-09-03 23:34:45 +03:00
|
|
|
|
import androidx.compose.material.icons.filled.ArrowDropDown
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.material.icons.filled.Refresh
|
|
|
|
|
|
import androidx.compose.material3.CircularProgressIndicator
|
2026-09-04 01:07:09 +03:00
|
|
|
|
import androidx.compose.material3.DropdownMenu
|
2026-09-03 23:16:57 +03:00
|
|
|
|
import androidx.compose.material3.DropdownMenuItem
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
|
|
|
|
import androidx.compose.material3.FilterChip
|
|
|
|
|
|
import androidx.compose.material3.FilterChipDefaults
|
|
|
|
|
|
import androidx.compose.material3.HorizontalDivider
|
|
|
|
|
|
import androidx.compose.material3.Icon
|
|
|
|
|
|
import androidx.compose.material3.IconButton
|
|
|
|
|
|
import androidx.compose.material3.MaterialTheme
|
|
|
|
|
|
import androidx.compose.material3.Text
|
|
|
|
|
|
import androidx.compose.material3.TextButton
|
|
|
|
|
|
import androidx.compose.material3.TopAppBar
|
|
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
|
|
import androidx.compose.runtime.collectAsState
|
|
|
|
|
|
import androidx.compose.runtime.getValue
|
2026-09-03 23:16:57 +03:00
|
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
|
|
import androidx.compose.runtime.setValue
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
|
|
import androidx.compose.ui.Modifier
|
2026-09-03 23:34:45 +03:00
|
|
|
|
import androidx.compose.ui.draw.clip
|
2026-09-10 00:23:28 +03:00
|
|
|
|
import androidx.compose.ui.platform.LocalContext
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
|
|
import java.time.DayOfWeek
|
2026-09-03 20:53:17 +03:00
|
|
|
|
import java.time.LocalDate
|
2026-09-02 23:47:00 +03:00
|
|
|
|
import java.time.format.DateTimeFormatter
|
|
|
|
|
|
|
2026-09-10 00:23:28 +03:00
|
|
|
|
|
|
|
|
|
|
val hPadding = 16.dp
|
|
|
|
|
|
|
2026-09-02 23:47:00 +03:00
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
|
|
@Composable
|
|
|
|
|
|
fun ScheduleScreen(viewModel: ScheduleViewModel, onSwitchGroup: () -> Unit) {
|
2026-09-10 00:23:28 +03:00
|
|
|
|
|
2026-09-02 23:47:00 +03:00
|
|
|
|
val state by viewModel.uiState.collectAsState()
|
|
|
|
|
|
|
|
|
|
|
|
Column(Modifier.fillMaxSize()) {
|
|
|
|
|
|
TopAppBar(
|
2026-09-04 02:05:52 +03:00
|
|
|
|
title = { Text(state.groupName ?: "Расписание", style = MaterialTheme.typography.titleMedium) },
|
2026-09-02 23:47:00 +03:00
|
|
|
|
actions = {
|
|
|
|
|
|
TextButton(onClick = { onSwitchGroup() }) {
|
|
|
|
|
|
Text("Сменить группу")
|
|
|
|
|
|
}
|
|
|
|
|
|
IconButton(onClick = { viewModel.refresh() }) {
|
|
|
|
|
|
Icon(Icons.Default.Refresh, contentDescription = "Обновить")
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-09-04 01:22:35 +03:00
|
|
|
|
windowInsets = WindowInsets(0.dp)
|
2026-09-02 23:47:00 +03:00
|
|
|
|
)
|
2026-09-10 00:23:28 +03:00
|
|
|
|
Column(Modifier.fillMaxSize().padding(horizontal = 0.dp, vertical = 0.dp)) {
|
2026-09-02 23:47:00 +03:00
|
|
|
|
when {
|
|
|
|
|
|
state.isSyncing && state.schedule == null -> {
|
|
|
|
|
|
Row(
|
|
|
|
|
|
Modifier.fillMaxWidth(),
|
|
|
|
|
|
horizontalArrangement = Arrangement.Center
|
|
|
|
|
|
) {
|
|
|
|
|
|
CircularProgressIndicator()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
state.schedule == null -> {
|
|
|
|
|
|
Column(
|
|
|
|
|
|
Modifier.fillMaxWidth(),
|
|
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally
|
|
|
|
|
|
) {
|
|
|
|
|
|
Text(
|
|
|
|
|
|
"Не удалось загрузить расписание",
|
|
|
|
|
|
style = MaterialTheme.typography.bodyLarge
|
|
|
|
|
|
)
|
|
|
|
|
|
TextButton(onClick = { viewModel.refresh() }) {
|
|
|
|
|
|
Text("Повторить")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
|
|
val schedule = state.schedule!!
|
2026-09-03 20:53:17 +03:00
|
|
|
|
val weekDays = state.weekStart
|
|
|
|
|
|
?.let { ws -> schedule.periods.firstOrNull { it.weekStart == ws } }
|
|
|
|
|
|
?.let { schedule.daysOf(it) }
|
|
|
|
|
|
.orEmpty()
|
|
|
|
|
|
val day = schedule.days.firstOrNull { it.date == state.dayDate }
|
|
|
|
|
|
|
2026-09-02 23:47:00 +03:00
|
|
|
|
if (day == null) {
|
|
|
|
|
|
Text("Занятий нет", style = MaterialTheme.typography.bodyLarge)
|
|
|
|
|
|
} else {
|
2026-09-03 20:53:17 +03:00
|
|
|
|
val isTodayDay = day.date == state.todayDate
|
|
|
|
|
|
val dayLabel = if (isTodayDay) "Сегодня " else ""
|
2026-09-02 23:47:00 +03:00
|
|
|
|
|
2026-09-03 23:34:45 +03:00
|
|
|
|
Row(
|
2026-09-10 00:23:28 +03:00
|
|
|
|
Modifier.fillMaxWidth().padding(horizontal = hPadding),
|
2026-09-03 23:34:45 +03:00
|
|
|
|
verticalAlignment = Alignment.CenterVertically
|
|
|
|
|
|
) {
|
|
|
|
|
|
Text(
|
|
|
|
|
|
"$dayLabel${dayName(day.date.dayOfWeek)} · ${day.date.format(FULL_DATE)}",
|
|
|
|
|
|
style = MaterialTheme.typography.titleMedium,
|
|
|
|
|
|
modifier = Modifier.weight(1f)
|
|
|
|
|
|
)
|
|
|
|
|
|
WeekSelector(
|
|
|
|
|
|
periods = schedule.periods,
|
|
|
|
|
|
selectedWeekStart = state.weekStart,
|
|
|
|
|
|
todayWeekStart = state.todayDate?.let { schedule.periodOf(it)?.weekStart },
|
|
|
|
|
|
onSelect = viewModel::selectWeek
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2026-09-10 00:23:28 +03:00
|
|
|
|
HorizontalDivider(Modifier.padding(top = 8.dp, bottom = 0.dp, start = hPadding, end = hPadding))
|
2026-09-02 23:47:00 +03:00
|
|
|
|
|
|
|
|
|
|
DayChips(
|
2026-09-03 20:53:17 +03:00
|
|
|
|
days = weekDays,
|
|
|
|
|
|
selectedDate = state.dayDate,
|
|
|
|
|
|
todayDate = state.todayDate,
|
2026-09-02 23:47:00 +03:00
|
|
|
|
onSelect = viewModel::selectDay
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-09-10 00:23:28 +03:00
|
|
|
|
HorizontalDivider(Modifier.padding(top=0.dp, start = hPadding, end = hPadding))
|
2026-09-02 23:47:00 +03:00
|
|
|
|
|
|
|
|
|
|
if (day.intervals.all { it.lessons.isEmpty() }) {
|
|
|
|
|
|
Text("Занятий нет", style = MaterialTheme.typography.bodyLarge)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LazyColumn(
|
|
|
|
|
|
modifier = Modifier.weight(1f),
|
2026-09-10 00:23:28 +03:00
|
|
|
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
|
|
|
|
|
contentPadding = PaddingValues(vertical = 8.dp)
|
2026-09-02 23:47:00 +03:00
|
|
|
|
) {
|
|
|
|
|
|
itemsIndexed(day.intervals) { slotIndex, slot ->
|
|
|
|
|
|
LessonSlot(slotIndex = slotIndex, slot = slot)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-03 23:16:57 +03:00
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
2026-09-03 20:53:17 +03:00
|
|
|
|
@Composable
|
2026-09-03 23:16:57 +03:00
|
|
|
|
private fun WeekSelector(
|
2026-09-03 20:53:17 +03:00
|
|
|
|
periods: List<SchedulePeriod>,
|
|
|
|
|
|
selectedWeekStart: LocalDate?,
|
|
|
|
|
|
todayWeekStart: LocalDate?,
|
|
|
|
|
|
onSelect: (LocalDate) -> Unit
|
|
|
|
|
|
) {
|
2026-09-03 23:16:57 +03:00
|
|
|
|
val selected = periods.firstOrNull { it.weekStart == selectedWeekStart }
|
|
|
|
|
|
var expanded by remember { mutableStateOf(false) }
|
|
|
|
|
|
|
2026-09-04 01:07:09 +03:00
|
|
|
|
Box {
|
2026-09-03 23:34:45 +03:00
|
|
|
|
Row(
|
2026-09-03 23:16:57 +03:00
|
|
|
|
modifier = Modifier
|
2026-09-03 23:34:45 +03:00
|
|
|
|
.clip(MaterialTheme.shapes.small)
|
|
|
|
|
|
.clickable(enabled = true, onClick = { expanded = true })
|
|
|
|
|
|
.padding(horizontal = 10.dp, vertical = 8.dp),
|
|
|
|
|
|
verticalAlignment = Alignment.CenterVertically
|
|
|
|
|
|
) {
|
|
|
|
|
|
Text(
|
|
|
|
|
|
selected?.let(::weekLabel) ?: "Период",
|
|
|
|
|
|
style = MaterialTheme.typography.labelLarge,
|
|
|
|
|
|
color = MaterialTheme.colorScheme.primary
|
|
|
|
|
|
)
|
|
|
|
|
|
Icon(
|
|
|
|
|
|
imageVector = Icons.Default.ArrowDropDown,
|
|
|
|
|
|
contentDescription = null,
|
|
|
|
|
|
tint = MaterialTheme.colorScheme.primary
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2026-09-04 01:07:09 +03:00
|
|
|
|
DropdownMenu(
|
2026-09-03 23:16:57 +03:00
|
|
|
|
expanded = expanded,
|
2026-09-04 01:07:09 +03:00
|
|
|
|
onDismissRequest = { expanded = false },
|
|
|
|
|
|
modifier = Modifier.widthIn(min = 220.dp)
|
2026-09-03 23:16:57 +03:00
|
|
|
|
) {
|
|
|
|
|
|
periods.forEach { period ->
|
|
|
|
|
|
val isTodayWeek = todayWeekStart == period.weekStart
|
|
|
|
|
|
DropdownMenuItem(
|
|
|
|
|
|
text = {
|
2026-09-04 01:07:09 +03:00
|
|
|
|
Text(
|
|
|
|
|
|
weekLabel(period),
|
|
|
|
|
|
style = MaterialTheme.typography.bodyLarge
|
|
|
|
|
|
)
|
|
|
|
|
|
},
|
|
|
|
|
|
trailingIcon = if (isTodayWeek) {
|
|
|
|
|
|
{
|
2026-09-03 23:16:57 +03:00
|
|
|
|
Text(
|
2026-09-04 01:07:09 +03:00
|
|
|
|
"сегодня",
|
|
|
|
|
|
style = MaterialTheme.typography.labelMedium,
|
|
|
|
|
|
color = MaterialTheme.colorScheme.primary
|
2026-09-03 23:16:57 +03:00
|
|
|
|
)
|
|
|
|
|
|
}
|
2026-09-04 01:07:09 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
null
|
2026-09-03 23:16:57 +03:00
|
|
|
|
},
|
|
|
|
|
|
onClick = {
|
|
|
|
|
|
expanded = false
|
|
|
|
|
|
onSelect(period.weekStart)
|
|
|
|
|
|
}
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2026-09-03 20:53:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-02 23:47:00 +03:00
|
|
|
|
@Composable
|
|
|
|
|
|
private fun DayChips(
|
|
|
|
|
|
days: List<Day>,
|
2026-09-03 20:53:17 +03:00
|
|
|
|
selectedDate: LocalDate?,
|
|
|
|
|
|
todayDate: LocalDate?,
|
|
|
|
|
|
onSelect: (LocalDate) -> Unit
|
2026-09-02 23:47:00 +03:00
|
|
|
|
) {
|
2026-09-10 00:23:28 +03:00
|
|
|
|
LazyRow(horizontalArrangement = Arrangement.spacedBy(6.dp), contentPadding = PaddingValues(horizontal = hPadding, vertical = 2.dp),) {
|
2026-09-03 20:53:17 +03:00
|
|
|
|
items(days, key = { it.date.toEpochDay() }) { day ->
|
|
|
|
|
|
val selected = selectedDate == day.date
|
|
|
|
|
|
val isTodayChip = todayDate == day.date
|
2026-09-02 23:47:00 +03:00
|
|
|
|
FilterChip(
|
|
|
|
|
|
selected = selected,
|
2026-09-03 20:53:17 +03:00
|
|
|
|
onClick = { onSelect(day.date) },
|
2026-09-02 23:47:00 +03:00
|
|
|
|
label = {
|
|
|
|
|
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
|
|
|
|
|
Text(
|
|
|
|
|
|
dayName(day.date.dayOfWeek),
|
|
|
|
|
|
style = MaterialTheme.typography.labelMedium
|
|
|
|
|
|
)
|
|
|
|
|
|
Text(
|
|
|
|
|
|
day.date.format(CHIP_DATE),
|
|
|
|
|
|
style = MaterialTheme.typography.labelSmall,
|
|
|
|
|
|
color = if (selected) {
|
|
|
|
|
|
MaterialTheme.colorScheme.onSecondaryContainer
|
|
|
|
|
|
} else {
|
|
|
|
|
|
MaterialTheme.colorScheme.onSurfaceVariant
|
|
|
|
|
|
}
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
modifier = Modifier.padding(vertical = 2.dp),
|
|
|
|
|
|
border = if (isTodayChip && !selected) {
|
|
|
|
|
|
FilterChipDefaults.filterChipBorder(
|
|
|
|
|
|
borderColor = MaterialTheme.colorScheme.primary,
|
|
|
|
|
|
borderWidth = 1.dp,
|
|
|
|
|
|
enabled = true,
|
|
|
|
|
|
selected = false
|
|
|
|
|
|
)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
FilterChipDefaults.filterChipBorder(enabled = true, selected = selected)
|
|
|
|
|
|
},
|
|
|
|
|
|
shape = RoundedCornerShape(8.dp)
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-03 20:53:17 +03:00
|
|
|
|
private fun weekLabel(period: SchedulePeriod): String {
|
|
|
|
|
|
val first = period.weekStart
|
|
|
|
|
|
return "с ${first.format(CHIP_DATE)}"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-02 23:47:00 +03:00
|
|
|
|
@Composable
|
|
|
|
|
|
private fun LessonSlot(slotIndex: Int, slot: TimeSlot) {
|
2026-09-10 00:23:28 +03:00
|
|
|
|
val context = LocalContext.current
|
|
|
|
|
|
var indexColor = 0
|
2026-09-02 23:47:00 +03:00
|
|
|
|
Column(Modifier.fillMaxWidth()) {
|
2026-09-09 22:25:55 +03:00
|
|
|
|
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
|
|
|
|
|
HorizontalDivider(
|
2026-09-10 00:23:28 +03:00
|
|
|
|
modifier = Modifier.width(10.dp + hPadding).padding(start = hPadding),
|
2026-09-09 22:25:55 +03:00
|
|
|
|
color = MaterialTheme.colorScheme.primary
|
|
|
|
|
|
)
|
|
|
|
|
|
Text(
|
|
|
|
|
|
"${slotIndex + 1} пара · ${slot.start.format(TIME)}–${slot.end.format(TIME)}",
|
|
|
|
|
|
style = MaterialTheme.typography.labelMedium,
|
|
|
|
|
|
color = MaterialTheme.colorScheme.primary,
|
|
|
|
|
|
modifier = Modifier.padding(horizontal = 4.dp)
|
|
|
|
|
|
)
|
2026-09-10 00:23:28 +03:00
|
|
|
|
HorizontalDivider(color = MaterialTheme.colorScheme.primary, modifier = Modifier.weight(1f).padding(end = hPadding))
|
2026-09-09 22:25:55 +03:00
|
|
|
|
}
|
2026-09-02 23:47:00 +03:00
|
|
|
|
if (slot.lessons.isEmpty()) {
|
|
|
|
|
|
Text("—", style = MaterialTheme.typography.bodyMedium)
|
|
|
|
|
|
} else {
|
2026-09-10 00:23:28 +03:00
|
|
|
|
slot.lessons.forEachIndexed { index, lesson ->
|
|
|
|
|
|
val backgroundColor = if (indexColor % 2 == 0) {
|
|
|
|
|
|
MaterialTheme.colorScheme.surfaceBright
|
|
|
|
|
|
} else {
|
|
|
|
|
|
MaterialTheme.colorScheme.surfaceVariant
|
|
|
|
|
|
}
|
|
|
|
|
|
indexColor++
|
|
|
|
|
|
// XXX:
|
|
|
|
|
|
// Убрать SelectionContainer?
|
|
|
|
|
|
// Режим редактирования расписания?
|
|
|
|
|
|
// Меню на onClick?
|
|
|
|
|
|
// Меню на onLongClick?
|
|
|
|
|
|
SelectionContainer() {
|
|
|
|
|
|
Column(
|
|
|
|
|
|
Modifier.fillMaxWidth().padding(vertical = 2.dp).background(backgroundColor)
|
|
|
|
|
|
// XXX: combinedClickable?
|
|
|
|
|
|
//.combinedClickable(
|
|
|
|
|
|
// onClick = {
|
|
|
|
|
|
// Toast.makeText(
|
|
|
|
|
|
// context,
|
|
|
|
|
|
// lesson.raw,
|
|
|
|
|
|
// Toast.LENGTH_SHORT
|
|
|
|
|
|
// ).show()
|
|
|
|
|
|
// },
|
|
|
|
|
|
// onLongClick = {},
|
|
|
|
|
|
// // TODO: onLongClickLabel = "(Accessibility)"
|
|
|
|
|
|
//),
|
|
|
|
|
|
) {
|
|
|
|
|
|
var any = false
|
2026-09-05 00:21:39 +03:00
|
|
|
|
|
2026-09-10 00:23:28 +03:00
|
|
|
|
val meta = listOfNotNull(
|
|
|
|
|
|
if (lesson.type == LessonType.OTHER) {
|
|
|
|
|
|
null
|
|
|
|
|
|
} else {
|
|
|
|
|
|
lesson.type.label
|
|
|
|
|
|
},
|
|
|
|
|
|
lesson.subgroup,
|
|
|
|
|
|
lesson.teacher,
|
|
|
|
|
|
lesson.room
|
|
|
|
|
|
).filter { it.isNotBlank() }.joinToString(" · ")
|
2026-09-05 00:21:39 +03:00
|
|
|
|
|
2026-09-10 00:23:28 +03:00
|
|
|
|
if (meta.isNotEmpty()) {
|
|
|
|
|
|
Text(
|
|
|
|
|
|
meta,
|
|
|
|
|
|
style = MaterialTheme.typography.bodySmall,
|
|
|
|
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
|
|
|
|
modifier = Modifier.padding(horizontal = hPadding)
|
|
|
|
|
|
)
|
|
|
|
|
|
any = true
|
|
|
|
|
|
}
|
2026-09-05 00:21:39 +03:00
|
|
|
|
|
2026-09-10 00:23:28 +03:00
|
|
|
|
val subject = lesson.subject?.takeIf { it.isNotBlank() } ?: lesson.raw
|
|
|
|
|
|
if (subject.isNotEmpty()) {
|
|
|
|
|
|
Text(
|
|
|
|
|
|
subject,
|
|
|
|
|
|
style = MaterialTheme.typography.bodyLarge,
|
|
|
|
|
|
modifier = Modifier.padding(horizontal = hPadding)
|
|
|
|
|
|
)
|
|
|
|
|
|
any = true
|
|
|
|
|
|
}
|
2026-09-09 22:25:55 +03:00
|
|
|
|
|
2026-09-10 00:23:28 +03:00
|
|
|
|
if (!any) {
|
|
|
|
|
|
Text(
|
|
|
|
|
|
"пусто",
|
|
|
|
|
|
style = MaterialTheme.typography.bodySmall,
|
|
|
|
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
|
|
|
|
modifier = Modifier.padding(horizontal = hPadding)
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2026-09-05 00:21:39 +03:00
|
|
|
|
|
2026-09-10 00:23:28 +03:00
|
|
|
|
}
|
2026-09-02 23:47:00 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun dayName(dayOfWeek: DayOfWeek): String = when (dayOfWeek) {
|
|
|
|
|
|
DayOfWeek.MONDAY -> "Пн"
|
|
|
|
|
|
DayOfWeek.TUESDAY -> "Вт"
|
|
|
|
|
|
DayOfWeek.WEDNESDAY -> "Ср"
|
|
|
|
|
|
DayOfWeek.THURSDAY -> "Чт"
|
|
|
|
|
|
DayOfWeek.FRIDAY -> "Пт"
|
|
|
|
|
|
DayOfWeek.SATURDAY -> "Сб"
|
|
|
|
|
|
DayOfWeek.SUNDAY -> "Вс"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private val FULL_DATE: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
|
|
|
|
|
|
private val CHIP_DATE: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM")
|
2026-09-03 20:53:17 +03:00
|
|
|
|
private val TIME: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm")
|