diff --git a/.kotlin/sessions/kotlin-compiler-1697198284998868167.salive b/.kotlin/sessions/kotlin-compiler-1697198284998868167.salive deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt b/app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt index c7ea28afd..1ccde1f3c 100644 --- a/app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt +++ b/app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable @Serializable data class BaseResponse( @SerialName("code") - val code: Int, + val code: String, @SerialName("message") val message: String, @SerialName("data") diff --git a/app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt b/app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt new file mode 100644 index 000000000..ea8b25a31 --- /dev/null +++ b/app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt @@ -0,0 +1,24 @@ +package com.cherrish.android.core.util + +import java.time.DayOfWeek +import java.time.LocalDateTime + +fun DayOfWeek.daysUntil(other: DayOfWeek) = (other.value - value + 7) % 7 + +fun formatProcedureDay(scheduledAt: String): String { + val dateTime = LocalDateTime.parse(scheduledAt) + val month = dateTime.month.value + val day = dateTime.dayOfMonth + val dayOfWeek = dateTime.dayOfWeek.toKoreanName() + return "${month}월 ${day}일 $dayOfWeek" +} + +fun DayOfWeek.toKoreanName(): String = when (this) { + DayOfWeek.MONDAY -> "월요일" + DayOfWeek.TUESDAY -> "화요일" + DayOfWeek.WEDNESDAY -> "수요일" + DayOfWeek.THURSDAY -> "목요일" + DayOfWeek.FRIDAY -> "금요일" + DayOfWeek.SATURDAY -> "토요일" + DayOfWeek.SUNDAY -> "일요일" +} diff --git a/app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt b/app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt index 7377d438f..7b7d851d5 100644 --- a/app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt +++ b/app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt @@ -1,7 +1,7 @@ package com.cherrish.android.data.di -import com.cherrish.android.data.remote.datasource.DummyDataSource -import com.cherrish.android.data.remote.datasourceimpl.DummyDataSourceImpl +import com.cherrish.android.data.remote.datasource.CalendarDataSource +import com.cherrish.android.data.remote.datasourceimpl.CalendarDataSourceImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -13,7 +13,7 @@ import javax.inject.Singleton abstract class DataSourceModule { @Binds @Singleton - abstract fun bindDummyDataSource( - dummyDataSourceImpl: DummyDataSourceImpl - ): DummyDataSource + abstract fun bindCalendarDataSource( + calendarDataSourceImpl: CalendarDataSourceImpl + ): CalendarDataSource } diff --git a/app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt b/app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt index ad16a49e8..ce7225f16 100644 --- a/app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt +++ b/app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt @@ -1,7 +1,7 @@ package com.cherrish.android.data.di -import com.cherrish.android.data.repository.DummyRepository -import com.cherrish.android.data.repositoryimpl.DummyRepositoryImpl +import com.cherrish.android.data.repository.CalendarRepository +import com.cherrish.android.data.repositoryimpl.CalendarRepositoryImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -13,7 +13,7 @@ import javax.inject.Singleton abstract class RepositoryModule { @Binds @Singleton - abstract fun bindDummyRepository( - dummyRepositoryImpl: DummyRepositoryImpl - ): DummyRepository + abstract fun bindCalendarRepository( + calendarRepositoryImpl: CalendarRepositoryImpl + ): CalendarRepository } diff --git a/app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt b/app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt index 22eba1dd8..9bc6bbde6 100644 --- a/app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt +++ b/app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt @@ -1,6 +1,6 @@ package com.cherrish.android.data.di -import com.cherrish.android.data.remote.service.DummyService +import com.cherrish.android.data.remote.service.CalendarService import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -13,7 +13,7 @@ import retrofit2.Retrofit object ServiceModule { @Provides @Singleton - fun provideDummyService( + fun provideCalendarService( retrofit: Retrofit - ): DummyService = retrofit.create(DummyService::class.java) + ): CalendarService = retrofit.create(CalendarService::class.java) } diff --git a/app/src/main/java/com/cherrish/android/data/model/CalendarDailyResponseModel.kt b/app/src/main/java/com/cherrish/android/data/model/CalendarDailyResponseModel.kt new file mode 100644 index 000000000..78a8561f3 --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/model/CalendarDailyResponseModel.kt @@ -0,0 +1,32 @@ +package com.cherrish.android.data.model + +import com.cherrish.android.data.remote.dto.response.CalendarDailyResponseDto +import com.cherrish.android.data.remote.dto.response.EventDto + +data class CalendarDailyResponseModel( + val eventCount: Int, + val events: List +) + +data class EventModel( + val type: String, + val userProcedureId: Long, + val procedureId: Long, + val name: String, + val scheduledAt: String, + val downtimeDays: Int +) + +fun CalendarDailyResponseDto.toModel() = CalendarDailyResponseModel( + eventCount = this.eventCount, + events = this.events.map { it.toModel() } +) + +fun EventDto.toModel() = EventModel( + type = this.type, + userProcedureId = this.userProcedureId, + procedureId = this.procedureId, + name = this.name, + scheduledAt = this.scheduledAt, + downtimeDays = this.downtimeDays +) diff --git a/app/src/main/java/com/cherrish/android/data/model/CalendarDownTimeResponseModel.kt b/app/src/main/java/com/cherrish/android/data/model/CalendarDownTimeResponseModel.kt new file mode 100644 index 000000000..1141d81cf --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/model/CalendarDownTimeResponseModel.kt @@ -0,0 +1,21 @@ +package com.cherrish.android.data.model + +import com.cherrish.android.data.remote.dto.response.CalendarDownTimeResponseDto + +data class CalendarDownTimeResponseModel( + val userProcedureId: Long, + val scheduledAt: String, + val downtimeDays: Int, + val sensitiveDays: List, + val cautionDays: List, + val recoveryDays: List +) + +fun CalendarDownTimeResponseDto.toModel() = CalendarDownTimeResponseModel( + userProcedureId = this.userProcedureId, + scheduledAt = this.scheduledAt, + downtimeDays = this.downtimeDays, + sensitiveDays = this.sensitiveDays, + cautionDays = this.cautionDays, + recoveryDays = this.recoveryDays +) diff --git a/app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyRequestModel.kt b/app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyRequestModel.kt new file mode 100644 index 000000000..d84d516f6 --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyRequestModel.kt @@ -0,0 +1,13 @@ +package com.cherrish.android.data.model + +import com.cherrish.android.data.remote.dto.request.CalendarMonthlyRequestDto + +data class CalendarMonthlyRequestModel( + val year: Int, + val month: Int +) + +fun CalendarMonthlyRequestModel.toDto() = CalendarMonthlyRequestDto( + year = this.year, + month = this.month +) diff --git a/app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyResponseModel.kt b/app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyResponseModel.kt new file mode 100644 index 000000000..0e771764c --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyResponseModel.kt @@ -0,0 +1,11 @@ +package com.cherrish.android.data.model + +import com.cherrish.android.data.remote.dto.response.CalendarMonthlyResponseDto + +data class CalendarMonthlyResponseModel( + val dailyProcedureCounts: Map +) + +fun CalendarMonthlyResponseDto.toModel() = CalendarMonthlyResponseModel( + dailyProcedureCounts = this.dailyProcedureCounts +) diff --git a/app/src/main/java/com/cherrish/android/data/remote/datasource/CalendarDataSource.kt b/app/src/main/java/com/cherrish/android/data/remote/datasource/CalendarDataSource.kt new file mode 100644 index 000000000..b84b361e9 --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/remote/datasource/CalendarDataSource.kt @@ -0,0 +1,12 @@ +package com.cherrish.android.data.remote.datasource + +import com.cherrish.android.core.network.BaseResponse +import com.cherrish.android.data.remote.dto.response.CalendarDailyResponseDto +import com.cherrish.android.data.remote.dto.response.CalendarDownTimeResponseDto +import com.cherrish.android.data.remote.dto.response.CalendarMonthlyResponseDto + +interface CalendarDataSource { + suspend fun getCalendarMonthly(year: Int, month: Int): BaseResponse + suspend fun getCalendarDaily(date: String): BaseResponse + suspend fun getCalendarEventDowntime(id: Long): BaseResponse +} diff --git a/app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/CalendarDataSourceImpl.kt b/app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/CalendarDataSourceImpl.kt new file mode 100644 index 000000000..e521c39d5 --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/CalendarDataSourceImpl.kt @@ -0,0 +1,29 @@ +package com.cherrish.android.data.remote.datasourceimpl + +import com.cherrish.android.core.network.BaseResponse +import com.cherrish.android.data.remote.datasource.CalendarDataSource +import com.cherrish.android.data.remote.dto.response.CalendarDailyResponseDto +import com.cherrish.android.data.remote.dto.response.CalendarDownTimeResponseDto +import com.cherrish.android.data.remote.dto.response.CalendarMonthlyResponseDto +import com.cherrish.android.data.remote.service.CalendarService +import javax.inject.Inject + +class CalendarDataSourceImpl @Inject constructor( + private val calendarService: CalendarService +) : CalendarDataSource { + override suspend fun getCalendarMonthly( + year: Int, + month: Int + ): BaseResponse = + calendarService.getCalendarMonthly(year = year, month = month) + + override suspend fun getCalendarDaily( + date: String + ): BaseResponse = + calendarService.getCalendarDaily(date = date) + + override suspend fun getCalendarEventDowntime( + id: Long + ): BaseResponse = + calendarService.getCalendarEventDowntime(id = id) +} diff --git a/app/src/main/java/com/cherrish/android/data/remote/dto/request/.gitkeep b/app/src/main/java/com/cherrish/android/data/remote/dto/request/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarDailyRequestDto.kt b/app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarDailyRequestDto.kt new file mode 100644 index 000000000..56ac8e6a6 --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarDailyRequestDto.kt @@ -0,0 +1,10 @@ +package com.cherrish.android.data.remote.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarDailyRequestDto( + @SerialName("date") + val date: String +) diff --git a/app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarMonthlyRequestDto.kt b/app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarMonthlyRequestDto.kt new file mode 100644 index 000000000..1be823471 --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarMonthlyRequestDto.kt @@ -0,0 +1,12 @@ +package com.cherrish.android.data.remote.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarMonthlyRequestDto( + @SerialName("year") + val year: Int, + @SerialName("month") + val month: Int +) diff --git a/app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDailyResponseDto.kt b/app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDailyResponseDto.kt new file mode 100644 index 000000000..061589c1b --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDailyResponseDto.kt @@ -0,0 +1,28 @@ +package com.cherrish.android.data.remote.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarDailyResponseDto( + @SerialName("eventCount") + val eventCount: Int, + @SerialName("events") + val events: List +) + +@Serializable +data class EventDto( + @SerialName("type") + val type: String, + @SerialName("userProcedureId") + val userProcedureId: Long, + @SerialName("procedureId") + val procedureId: Long, + @SerialName("name") + val name: String, + @SerialName("scheduledAt") + val scheduledAt: String, + @SerialName("downtimeDays") + val downtimeDays: Int +) diff --git a/app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDownTimeResponseDto.kt b/app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDownTimeResponseDto.kt new file mode 100644 index 000000000..18d0e40cc --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDownTimeResponseDto.kt @@ -0,0 +1,20 @@ +package com.cherrish.android.data.remote.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarDownTimeResponseDto( + @SerialName("userProcedureId") + val userProcedureId: Long, + @SerialName("scheduledAt") + val scheduledAt: String, + @SerialName("downtimeDays") + val downtimeDays: Int, + @SerialName("sensitiveDays") + val sensitiveDays: List, + @SerialName("cautionDays") + val cautionDays: List, + @SerialName("recoveryDays") + val recoveryDays: List +) diff --git a/app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarMonthlyResponseDto.kt b/app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarMonthlyResponseDto.kt new file mode 100644 index 000000000..bbeaf6d1c --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarMonthlyResponseDto.kt @@ -0,0 +1,10 @@ +package com.cherrish.android.data.remote.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarMonthlyResponseDto( + @SerialName("dailyProcedureCounts") + val dailyProcedureCounts: Map +) diff --git a/app/src/main/java/com/cherrish/android/data/remote/service/CalendarService.kt b/app/src/main/java/com/cherrish/android/data/remote/service/CalendarService.kt new file mode 100644 index 000000000..5ad6aba8b --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/remote/service/CalendarService.kt @@ -0,0 +1,27 @@ +package com.cherrish.android.data.remote.service + +import com.cherrish.android.core.network.BaseResponse +import com.cherrish.android.data.remote.dto.response.CalendarDailyResponseDto +import com.cherrish.android.data.remote.dto.response.CalendarDownTimeResponseDto +import com.cherrish.android.data.remote.dto.response.CalendarMonthlyResponseDto +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query + +interface CalendarService { + @GET("api/calendar/monthly") + suspend fun getCalendarMonthly( + @Query("year") year: Int, + @Query("month") month: Int + ): BaseResponse + + @GET("api/calendar/daily") + suspend fun getCalendarDaily( + @Query("date") date: String + ): BaseResponse + + @GET("api/calendar/events/{id}/downtime") + suspend fun getCalendarEventDowntime( + @Path("id") id: Long + ): BaseResponse +} diff --git a/app/src/main/java/com/cherrish/android/data/repository/CalendarRepository.kt b/app/src/main/java/com/cherrish/android/data/repository/CalendarRepository.kt new file mode 100644 index 000000000..323b8f135 --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/repository/CalendarRepository.kt @@ -0,0 +1,20 @@ +package com.cherrish.android.data.repository + +import com.cherrish.android.data.model.CalendarDailyResponseModel +import com.cherrish.android.data.model.CalendarDownTimeResponseModel +import com.cherrish.android.data.model.CalendarMonthlyResponseModel + +interface CalendarRepository { + suspend fun getCalendarMonthly( + year: Int, + month: Int + ): Result + + suspend fun getCalendarDaily( + date: String + ): Result + + suspend fun getCalendarEventDowntime( + id: Long + ): Result +} diff --git a/app/src/main/java/com/cherrish/android/data/repositoryimpl/CalendarRepositoryImpl.kt b/app/src/main/java/com/cherrish/android/data/repositoryimpl/CalendarRepositoryImpl.kt new file mode 100644 index 000000000..f19ef98ab --- /dev/null +++ b/app/src/main/java/com/cherrish/android/data/repositoryimpl/CalendarRepositoryImpl.kt @@ -0,0 +1,32 @@ +package com.cherrish.android.data.repositoryimpl + +import com.cherrish.android.core.util.suspendRunCatching +import com.cherrish.android.data.model.CalendarDailyResponseModel +import com.cherrish.android.data.model.CalendarDownTimeResponseModel +import com.cherrish.android.data.model.CalendarMonthlyResponseModel +import com.cherrish.android.data.model.toModel +import com.cherrish.android.data.remote.datasource.CalendarDataSource +import com.cherrish.android.data.repository.CalendarRepository +import javax.inject.Inject + +class CalendarRepositoryImpl @Inject constructor( + private val calendarDataSource: CalendarDataSource +) : CalendarRepository { + override suspend fun getCalendarMonthly( + year: Int, + month: Int + ): Result = + suspendRunCatching { + calendarDataSource.getCalendarMonthly(year = year, month = month).data!!.toModel() + } + + override suspend fun getCalendarDaily(date: String): Result = + suspendRunCatching { + calendarDataSource.getCalendarDaily(date = date).data!!.toModel() + } + + override suspend fun getCalendarEventDowntime(id: Long): Result = + suspendRunCatching { + calendarDataSource.getCalendarEventDowntime(id = id).data!!.toModel() + } +} diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt index a8e214e20..6cffc70d4 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt @@ -39,7 +39,7 @@ fun CalendarRoute( CalendarScreen( uiState = state.data, paddingValues = paddingValues, - onMonthChange = viewModel::onMonthChanged, + onMonthChange = viewModel::onMonthChange, onDateClick = viewModel::onDateClick, onEventClick = viewModel::onEventClick, onAddButtonClick = { /*TODO: 시술 선택 플로우로 이동*/ } diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarUiState.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarUiState.kt index 4d2397f9e..5c732cb00 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarUiState.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarUiState.kt @@ -14,7 +14,8 @@ data class CalendarUiState( val selectedYearMonth: YearMonth = YearMonth.now(), val calendarDisplayMode: CalendarDisplayMode = CalendarDisplayMode.Normal(), val selectedDate: LocalDate? = LocalDate.now(), - val procedureInfoList: ImmutableList = persistentListOf() + val procedureInfoList: ImmutableList = persistentListOf(), + val cachedProcedureCountByDate: Map = emptyMap() ) { companion object { private val today = LocalDate.of(2026, 1, 7) diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt index 479ed7865..26c287e4a 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt @@ -1,38 +1,239 @@ package com.cherrish.android.presentation.calendar import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.cherrish.android.core.common.extension.onLogFailure import com.cherrish.android.core.common.extension.updateSuccess import com.cherrish.android.core.common.state.UiState +import com.cherrish.android.core.util.formatProcedureDay +import com.cherrish.android.data.repository.CalendarRepository +import com.cherrish.android.presentation.calendar.model.CalendarDisplayMode +import com.cherrish.android.presentation.calendar.model.DownTimeStatus +import com.cherrish.android.presentation.calendar.model.ProcedureInfoModel import dagger.hilt.android.lifecycle.HiltViewModel import java.time.LocalDate import java.time.YearMonth import javax.inject.Inject +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch @HiltViewModel -class CalendarViewModel @Inject constructor() : ViewModel() { - - private val _uiState = MutableStateFlow>( - UiState.Success(CalendarUiState.FakeNormal) - ) +class CalendarViewModel @Inject constructor( + private val calendarRepository: CalendarRepository +) : ViewModel() { + private val _uiState = MutableStateFlow>(UiState.Loading) val uiState: StateFlow> = _uiState.asStateFlow() - fun onMonthChanged(yearMonth: YearMonth) { + private val monthlyCache = mutableMapOf>() + private val dailyCache = mutableMapOf>() + + init { + loadMonthlyCalendar(yearMonth = YearMonth.now()) + } + + fun onDateClick(date: LocalDate) { _uiState.updateSuccess { currentState -> - currentState.copy( - selectedYearMonth = yearMonth, - selectedDate = null, - procedureInfoList = persistentListOf() - ) + if (currentState.calendarDisplayMode is CalendarDisplayMode.Downtime) { + viewModelScope.launch { + calendarRepository.getCalendarDaily( + date = date.toString() + ).onSuccess { response -> + val procedureList = response.events.map { event -> + ProcedureInfoModel( + procedureId = event.userProcedureId, + procedureName = event.name, + procedureDay = formatProcedureDay(event.scheduledAt), + downTimeDuration = event.downtimeDays + ) + }.toImmutableList() + + dailyCache[date] = procedureList + + _uiState.updateSuccess { state -> + state.copy( + calendarDisplayMode = CalendarDisplayMode.Normal( + procedureCountByDate = state.cachedProcedureCountByDate + ), + selectedDate = date, + procedureInfoList = procedureList + ) + } + }.onLogFailure { } + } + currentState + } else { + loadDailyCalendar(date) + currentState.copy(selectedDate = date) + } } } - fun onDateClick(date: LocalDate) { + fun onMonthChange(yearMonth: YearMonth) { + val newSelectedDate = if (yearMonth == YearMonth.now()) { + LocalDate.now() + } else { + yearMonth.atDay(1) + } + + loadMonthlyCalendar(yearMonth, newSelectedDate) } fun onEventClick(procedureId: Long) { + _uiState.updateSuccess { currentState -> + val currentMode = currentState.calendarDisplayMode + + if (currentMode is CalendarDisplayMode.Downtime) { + currentState.copy( + calendarDisplayMode = CalendarDisplayMode.Normal( + procedureCountByDate = currentState.cachedProcedureCountByDate + ) + ) + } else { + loadDowntimeDetail(procedureId) + currentState + } + } + } + + private fun loadMonthlyCalendar( + yearMonth: YearMonth, + selectedDate: LocalDate = LocalDate.now() + ) { + val cachedMonthlyData = monthlyCache[yearMonth] + val cachedDailyData = dailyCache[selectedDate] + + if (cachedMonthlyData != null) { + _uiState.update { + UiState.Success( + CalendarUiState( + selectedYearMonth = yearMonth, + calendarDisplayMode = CalendarDisplayMode.Normal(cachedMonthlyData), + selectedDate = selectedDate, + procedureInfoList = cachedDailyData ?: persistentListOf(), + cachedProcedureCountByDate = cachedMonthlyData + ) + ) + } + + if (cachedDailyData == null) { + loadDailyCalendar(selectedDate) + } + return + } + + viewModelScope.launch { + val dailyDeferred = async { + calendarRepository.getCalendarDaily(date = selectedDate.toString()) + } + + calendarRepository.getCalendarMonthly( + year = yearMonth.year, + month = yearMonth.monthValue + ).onSuccess { response -> + val procedureCountByDate = response.dailyProcedureCounts.mapKeys { (day, _) -> + yearMonth.atDay(day) + }.mapValues { it.value.toInt() } + + monthlyCache[yearMonth] = procedureCountByDate + + dailyDeferred.await().onSuccess { dailyResponse -> + val procedureList = dailyResponse.events.map { event -> + ProcedureInfoModel( + procedureId = event.userProcedureId, + procedureName = event.name, + procedureDay = formatProcedureDay(event.scheduledAt), + downTimeDuration = event.downtimeDays + ) + }.toImmutableList() + + dailyCache[selectedDate] = procedureList + + _uiState.update { + UiState.Success( + CalendarUiState( + selectedYearMonth = yearMonth, + calendarDisplayMode = CalendarDisplayMode.Normal( + procedureCountByDate + ), + selectedDate = selectedDate, + procedureInfoList = procedureList, + cachedProcedureCountByDate = procedureCountByDate + ) + ) + } + } + }.onLogFailure { } + } + } + + private fun loadDailyCalendar(date: LocalDate) { + val cachedData = dailyCache[date] + if (cachedData != null) { + _uiState.updateSuccess { currentState -> + currentState.copy( + selectedDate = date, + procedureInfoList = cachedData + ) + } + return + } + + viewModelScope.launch { + calendarRepository.getCalendarDaily( + date = date.toString() + ).onSuccess { response -> + val procedureList = response.events.map { event -> + ProcedureInfoModel( + procedureId = event.userProcedureId, + procedureName = event.name, + procedureDay = formatProcedureDay(event.scheduledAt), + downTimeDuration = event.downtimeDays + ) + }.toImmutableList() + + dailyCache[date] = procedureList + + _uiState.updateSuccess { currentState -> + currentState.copy( + selectedDate = date, + procedureInfoList = procedureList + ) + } + }.onLogFailure { } + } + } + + private fun loadDowntimeDetail(userProcedureId: Long) { + viewModelScope.launch { + calendarRepository.getCalendarEventDowntime(userProcedureId).onSuccess { response -> + _uiState.updateSuccess { currentState -> + val downtimeByDate = buildMap { + response.sensitiveDays.forEach { dateString -> + put(LocalDate.parse(dateString), DownTimeStatus.SENSITIVE) + } + response.cautionDays.forEach { dateString -> + put(LocalDate.parse(dateString), DownTimeStatus.CAUTION) + } + response.recoveryDays.forEach { dateString -> + put(LocalDate.parse(dateString), DownTimeStatus.RECOVERY) + } + } + + currentState.copy( + calendarDisplayMode = CalendarDisplayMode.Downtime( + downtimeByDate = downtimeByDate, + selectedProcedureId = userProcedureId + ) + ) + } + }.onLogFailure { } + } } } diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt index f9c57cd51..ea298c8e4 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt @@ -30,16 +30,19 @@ import com.cherrish.android.presentation.calendar.util.getProcedureColors fun ProcedureInfoItem( procedureName: String, procedureDay: String, - downTimeDuration: Int?, + downTimeDuration: Int, onClick: () -> Unit, modifier: Modifier = Modifier, - procedureType: ProcedureType = ProcedureType.ACTIVE + procedureType: ProcedureType = ProcedureType.ACTIVE, + isDowntimeMode: Boolean = false ) { val themeColors = CherrishTheme.colors val colors = remember(procedureType, themeColors) { getProcedureColors(procedureType, themeColors) } + val isClickable = isDowntimeMode || downTimeDuration != 0 + Row( modifier = modifier .fillMaxWidth() @@ -50,7 +53,13 @@ fun ProcedureInfoItem( color = colors.border, shape = RoundedCornerShape(6.dp) ) - .noRippleClickable(onClick = onClick) + .then( + if (isClickable) { + Modifier.noRippleClickable(onClick = onClick) + } else { + Modifier + } + ) .padding(vertical = 11.dp, horizontal = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp) @@ -112,7 +121,9 @@ private fun ProcedureScheduleInfo( color = colors.procedureDateText ) Text( - text = downTimeDuration?.let { "다운타임 ${it}일" } ?: "-", + text = downTimeDuration.let { + if (it == 0) "-" else "다운타임 ${it}일" + }, style = CherrishTheme.typography.body3R12, color = colors.procedureDateText ) @@ -147,7 +158,7 @@ private fun ProcedureInfoItemPreview() { procedureType = ProcedureType.INACTIVE, procedureName = "레이저토닝", procedureDay = "1월 7일 수요일", - downTimeDuration = null, + downTimeDuration = 0, onClick = {} ) } diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt index 1f980f7a3..b482811d9 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt @@ -45,6 +45,8 @@ fun ProcedureScheduleCard( onAddProcedureClick: () -> Unit, modifier: Modifier = Modifier ) { + val isDowntimeMode = displayMode is CalendarDisplayMode.Downtime + val listState = rememberLazyListState() val isFirstItemVisible = remember { @@ -115,6 +117,7 @@ fun ProcedureScheduleCard( procedure.procedureId, procedure.downTimeDuration ), + isDowntimeMode = isDowntimeMode, onClick = { onProcedureClick(procedure.procedureId) } ) } diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/model/ProcedureInfoModel.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/model/ProcedureInfoModel.kt index 473edd8b4..73540f1c1 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/model/ProcedureInfoModel.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/model/ProcedureInfoModel.kt @@ -7,5 +7,5 @@ data class ProcedureInfoModel( val procedureId: Long, val procedureName: String, val procedureDay: String, - val downTimeDuration: Int? + val downTimeDuration: Int ) diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt deleted file mode 100644 index 172d164aa..000000000 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.cherrish.android.presentation.calendar.util - -import java.time.DayOfWeek - -fun DayOfWeek.daysUntil(other: DayOfWeek) = (other.value - value + 7) % 7 diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt index ec6018e2b..780580456 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt @@ -1,5 +1,6 @@ package com.cherrish.android.presentation.calendar.util +import com.cherrish.android.core.util.daysUntil import com.cherrish.android.presentation.calendar.model.CalendarDay import com.cherrish.android.presentation.calendar.model.CalendarDisplayMode import com.cherrish.android.presentation.calendar.model.CalendarMonth diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/util/ProcedureUtil.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/util/ProcedureUtil.kt index 49575eb3f..e05733757 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/util/ProcedureUtil.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/util/ProcedureUtil.kt @@ -47,10 +47,10 @@ fun getProcedureColors( fun getProcedureType( displayMode: CalendarDisplayMode, procedureId: Long, - downTimeDuration: Int? + downTimeDuration: Int ): ProcedureType = when (displayMode) { is CalendarDisplayMode.Normal -> { - if (downTimeDuration == null) { + if (downTimeDuration == 0) { ProcedureType.ACTIVE_NO_DOWNTIME } else { ProcedureType.ACTIVE