Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.yapp.dataapi

import com.yapp.model.ScheduleList
import com.yapp.model.Sessions
import kotlinx.coroutines.flow.Flow

interface SessionRepository {
interface ScheduleRepository {
fun getSessions(): Flow<List<Sessions>>

fun getSchedules(year: Int, month: Int): Flow<ScheduleList>
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import com.yapp.core.data.data.repository.AttendanceRepositoryImpl
import com.yapp.core.data.data.repository.AuthRepositoryImpl
import com.yapp.core.data.data.repository.OperationsRepositoryImpl
import com.yapp.core.data.data.repository.PostsRepositoryImpl
import com.yapp.core.data.data.repository.SessionsRepositoryImpl
import com.yapp.core.data.data.repository.ScheduleRepositoryImpl
import com.yapp.core.data.data.repository.UserRepositoryImpl
import com.yapp.dataapi.AlarmRepository
import com.yapp.dataapi.AttendanceRepository
import com.yapp.dataapi.AuthRepository
import com.yapp.dataapi.OperationsRepository
import com.yapp.dataapi.PostsRepository
import com.yapp.dataapi.SessionRepository
import com.yapp.dataapi.ScheduleRepository
import com.yapp.dataapi.UserRepository
import dagger.Binds
import dagger.Module
Expand Down Expand Up @@ -55,6 +55,6 @@ internal abstract class RepositoryModule {

@Binds
abstract fun bindsSessionRepositoryImpl(
repositoryImpl: SessionsRepositoryImpl
): SessionRepository
repositoryImpl: ScheduleRepositoryImpl
): ScheduleRepository
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.yapp.core.data.data.repository

import com.yapp.core.data.remote.api.SessionApi
import com.yapp.dataapi.SessionRepository
import com.yapp.core.data.remote.api.ScheduleApi
import com.yapp.dataapi.ScheduleRepository
import com.yapp.model.Sessions
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

internal class SessionsRepositoryImpl @Inject constructor(
private val sessionApi: SessionApi
): SessionRepository{
internal class ScheduleRepositoryImpl @Inject constructor(
private val scheduleApi: ScheduleApi
): ScheduleRepository{
override fun getSessions() = flow {
emit(sessionApi.getSessions().map { result ->
emit(scheduleApi.getSessions().map { result ->
Sessions(
id = result.id,
name = result.name,
Expand All @@ -23,4 +23,8 @@ internal class SessionsRepositoryImpl @Inject constructor(
)
})
}

override fun getSchedules(year: Int, month: Int) = flow {
emit(scheduleApi.getSchedules(year, month).toScheduleListModel())
}
Comment on lines +27 to +29
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

getSchedules 메서드도 에러 처리가 필요합니다.

API 호출 시 발생할 수 있는 오류에 대한 처리가 누락되어 있습니다.

다음과 같이 에러 처리를 추가하는 것을 고려해보세요:

override fun getSchedules(year: Int, month: Int) = flow {
+   try {
        emit(scheduleApi.getSchedules(year, month).toScheduleListModel())
+   } catch (e: Exception) {
+       // 적절한 에러 로깅
+       throw e
+   }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
override fun getSchedules(year: Int, month: Int) = flow {
emit(scheduleApi.getSchedules(year, month).toScheduleListModel())
}
override fun getSchedules(year: Int, month: Int) = flow {
try {
emit(scheduleApi.getSchedules(year, month).toScheduleListModel())
} catch (e: Exception) {
// 적절한 에러 로깅
throw e
}
}

🧹 Nitpick (assertive)

선택적: getSchedules의 파라미터 유효성 검사 추가

year와 month 파라미터에 대한 유효성 검사를 추가하면 API 호출 전에 잠재적인 문제를 방지할 수 있습니다.

override fun getSchedules(year: Int, month: Int) = flow {
+   // 유효성 검사: month는 1-12 사이여야 함
+   require(month in 1..12) { "Month must be between 1 and 12" }
+   // 유효성 검사: year는 합리적인 범위여야 함 (예: 2000년 이후)
+   require(year >= 2000) { "Year must be 2000 or later" }
    
    emit(scheduleApi.getSchedules(year, month).toScheduleListModel())
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
override fun getSchedules(year: Int, month: Int) = flow {
emit(scheduleApi.getSchedules(year, month).toScheduleListModel())
}
override fun getSchedules(year: Int, month: Int) = flow {
// 유효성 검사: month는 1-12 사이여야 함
require(month in 1..12) { "Month must be between 1 and 12" }
// 유효성 검사: year는 합리적인 범위여야 함 (예: 2000년 이후)
require(year >= 2000) { "Year must be 2000 or later" }
emit(scheduleApi.getSchedules(year, month).toScheduleListModel())
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.yapp.core.data.remote.api

import com.yapp.core.data.remote.model.response.DateGroupedScheduleResponse
import com.yapp.core.data.remote.model.response.SessionResponse
import retrofit2.http.GET
import retrofit2.http.Query

interface ScheduleApi {
@GET("v1/sessions")
suspend fun getSessions(): List<SessionResponse>

@GET("v1/schedules")
suspend fun getSchedules(
@Query("year") year: Int,
@Query("month") month: Int,
): DateGroupedScheduleResponse
}
Comment on lines +8 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

API 인터페이스 문서화 추가 권장

API 인터페이스에 KDoc 주석을 추가하여 각 엔드포인트의 목적과 반환되는 데이터를 설명하는 것이 좋습니다. 이는 다른 개발자들이 API를 더 쉽게 이해하고 사용할 수 있게 도와줍니다.

또한 getSchedules 메서드의 yearmonth 매개변수에 대한 유효한 범위나 형식에 대한 정보를 문서화하면 좋을 것 같습니다.

interface ScheduleApi {
    /**
     * 모든 세션 목록을 가져옵니다.
     * @return 세션 응답 목록
     */
    @GET("v1/sessions")
    suspend fun getSessions(): List<SessionResponse>

    /**
     * 특정 연도와 월의 일정을 그룹화하여 가져옵니다.
     * @param year 연도 (예: 2023)
     * @param month 월 (1-12)
     * @return 날짜별로 그룹화된 일정 응답
     */
    @GET("v1/schedules")
    suspend fun getSchedules(
        @Query("year") year: Int,
        @Query("month") month: Int,
    ): DateGroupedScheduleResponse
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ import com.yapp.core.data.remote.api.AttendanceApi
import com.yapp.core.data.remote.api.AuthApi
import com.yapp.core.data.remote.api.OperationsApi
import com.yapp.core.data.remote.api.PostsApi
import com.yapp.core.data.remote.api.SessionApi
import com.yapp.core.data.remote.api.ScheduleApi
import com.yapp.core.data.remote.api.UserApi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import retrofit2.create
import javax.inject.Singleton

@Module
Expand Down Expand Up @@ -57,7 +56,7 @@ internal object ApiModule {

@Singleton
@Provides
fun providesSessionsApi(@AuthRetrofit retrofit: Retrofit): SessionApi {
return retrofit.create(SessionApi::class.java)
fun providesSessionsApi(@AuthRetrofit retrofit: Retrofit): ScheduleApi {
return retrofit.create(ScheduleApi::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.yapp.core.data.remote.model.response

import com.yapp.model.AttendanceStatus
import com.yapp.model.DateGroupedSchedule
import com.yapp.model.ScheduleInfo
import com.yapp.model.ScheduleList
import com.yapp.model.ScheduleProgressPhase
import com.yapp.model.ScheduleType
import com.yapp.model.SessionType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class DateGroupedScheduleResponse(
@SerialName("dates")
val dates: List<ScheduleListResponse>
) {
fun toScheduleListModel() = ScheduleList(
dates
.filter { it.schedules.isNotEmpty() }
.map { it.toScheduleListModel() }
)
}

@Serializable
data class ScheduleListResponse(
@SerialName("date")
val date: String,

@SerialName("isToday")
val isToday: Boolean,

@SerialName("dayOfTheWeek")
val dayOfTheWeek: String,

@SerialName("schedules")
val schedules: List<ScheduleResponse>,
) {
fun toScheduleListModel() = DateGroupedSchedule(
date = date,
isToday = isToday,
dayOfTheWeek = dayOfTheWeek,
schedules = schedules.map { it.toScheduleModel() }
)
}

@Serializable
data class ScheduleResponse(
@SerialName("id")
val id: String,

@SerialName("name")
val name: String,

@SerialName("place")
val place: String?,

@SerialName("date")
val date: String,

@SerialName("endDate")
val endDate: String?,

@SerialName("time")
val time: String?,

@SerialName("endTime")
val endTime: String?,

@SerialName("scheduleType")
val scheduleType: String,

@SerialName("sessionType")
val sessionType: String?,

@SerialName("scheduleProgressPhase")
val scheduleProgressPhase: String,

@SerialName("attendanceStatus")
val attendanceStatus: String?,
) {
fun toScheduleModel() = ScheduleInfo(
id = id,
name = name,
place = place,
date = date,
endDate = endDate,
time = time,
endTime = endTime,
scheduleType = scheduleType.toScheduleType(),
sessionType = sessionType.toSessionType(),
scheduleProgressPhase = scheduleProgressPhase.toScheduleProgressPhase(),
attendanceStatus = attendanceStatus.toAttendanceStatus(),
)

companion object {
fun String.toScheduleType() =
ScheduleType.entries.firstOrNull { it.name == this } ?: ScheduleType.ETC

fun String?.toSessionType() =
SessionType.entries.firstOrNull { it.name == this }

fun String?.toScheduleProgressPhase() =
ScheduleProgressPhase.entries.firstOrNull { it.name == this }
?: ScheduleProgressPhase.PENDING

fun String?.toAttendanceStatus() =
AttendanceStatus.entries.firstOrNull { it.name == this } ?: AttendanceStatus.SCHEDULED
}
}
42 changes: 42 additions & 0 deletions core/model/src/main/java/com/yapp/model/ScheduleInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.yapp.model

data class ScheduleList(
val dates: List<DateGroupedSchedule>
)

data class DateGroupedSchedule(
val date: String,
val isToday: Boolean,
val dayOfTheWeek: String,
val schedules: List<ScheduleInfo>
)

data class ScheduleInfo(
val id: String,
val name: String,
val place: String?,
val date: String,
val endDate: String?,
val time: String?,
val endTime: String?,
val scheduleType: ScheduleType,
val sessionType: SessionType?,
val scheduleProgressPhase: ScheduleProgressPhase,
val attendanceStatus: AttendanceStatus,
)
Comment on lines +14 to +26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

도메인 모델에서 날짜·시간을 문자열로 보관하면 추후 파싱 비용이 반복됩니다

date, endDate, time, endTime를 모두 String 타입으로 두면

  1. 레이어마다 파싱·포매팅 로직이 중복될 수 있고
  2. 잘못된 형식의 값이 런타임까지 흘러 들어올 위험이 있습니다.

LocalDate, LocalDateTime, LocalTime(java-time)이나 별도 value class 로 감싸 두면 타입 안전성과 재사용성이 모두 좋아집니다.
데이터->도메인 매핑 시 한 번만 파싱하면, UI 레이어에서는 그대로 format(...)만 호출하면 됩니다.
필요하시다면 적용 예시 패치를 도와드릴 수 있습니다.


enum class ScheduleType {
SESSION, TASK, ETC;
}

enum class SessionType {
OFFLINE, ONLINE, TEAM;
}

enum class ScheduleProgressPhase {
DONE, TODAY, ONGOING, PENDING;
}

enum class AttendanceStatus {
SCHEDULED, ATTENDED, LATE, ABSENT, EARLY_LEAVE, EXCUSED;
}
Comment on lines +28 to +42
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

enum 끝에 세미콜론은 생략 가능합니다

Kotlin 에서는 열거형 상수 선언 뒤 마지막 ;가 필요 없습니다.
불필요한 구두점을 제거하면 diff 노이즈가 줄어들어 유지보수가 수월해집니다.

-enum class ScheduleType {
-    SESSION, TASK, ETC;
-}
+enum class ScheduleType {
+    SESSION, TASK, ETC
+}

다른 enum 들도 동일하게 정리하면 좋겠습니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
enum class ScheduleType {
SESSION, TASK, ETC;
}
enum class SessionType {
OFFLINE, ONLINE, TEAM;
}
enum class ScheduleProgressPhase {
DONE, TODAY, ONGOING, PENDING;
}
enum class AttendanceStatus {
SCHEDULED, ATTENDED, LATE, ABSENT, EARLY_LEAVE, EXCUSED;
}
enum class ScheduleType {
SESSION, TASK, ETC
}
enum class SessionType {
OFFLINE, ONLINE, TEAM;
}
enum class ScheduleProgressPhase {
DONE, TODAY, ONGOING, PENDING;
}
enum class AttendanceStatus {
SCHEDULED, ATTENDED, LATE, ABSENT, EARLY_LEAVE, EXCUSED;
}

Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.yapp.core.designsystem.theme.YappTheme
import com.yapp.core.ui.R

enum class AttendanceStatus {
SCHEDULED,
ATTENDED,
LATE,
ABSENT,
EARLY_LEAVE,
EXCUSED,
}
import com.yapp.model.AttendanceStatus

@Composable
fun AttendanceStatusChip(
Expand Down
51 changes: 51 additions & 0 deletions core/ui/src/main/java/com/yapp/core/ui/util/DateTime.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.yapp.core.ui.util

import android.content.Context
import com.yapp.core.ui.R
import java.time.LocalDate
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.util.Locale

fun formatToDay(context: Context, date: String): String {
return try {
val parsedDate = LocalDate.parse(date)
context.getString(R.string.time_format_day, parsedDate.dayOfMonth)
} catch (e: Exception) {
date
}
}
Comment on lines +10 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

예외 처리를 개선하세요.

현재 코드에서는 너무 일반적인 Exception을 사용하여 예외를 캐치하고 있으며, 예외가 발생했을 때 아무런 로그도 남기지 않고 있습니다.

특정 예외를 캐치하고 적절한 로깅을 추가하는 것이 좋습니다:

  fun formatToDay(context: Context, date: String): String {
      return try {
          val parsedDate = LocalDate.parse(date)
          context.getString(R.string.time_format_day, parsedDate.dayOfMonth)
-     } catch (e: Exception) {
+     } catch (e: DateTimeParseException) {
+         android.util.Log.e("DateTime", "Failed to parse date: $date", e)
          date
      }
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun formatToDay(context: Context, date: String): String {
return try {
val parsedDate = LocalDate.parse(date)
context.getString(R.string.time_format_day, parsedDate.dayOfMonth)
} catch (e: Exception) {
date
}
}
fun formatToDay(context: Context, date: String): String {
return try {
val parsedDate = LocalDate.parse(date)
context.getString(R.string.time_format_day, parsedDate.dayOfMonth)
} catch (e: DateTimeParseException) {
android.util.Log.e("DateTime", "Failed to parse date: $date", e)
date
}
}
🧰 Tools
🪛 detekt (1.23.7)

[warning] 13-13: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)


[warning] 13-13: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


fun formatTimeRange(context: Context, startTime: String?, endTime: String?): String? {
if (startTime.isNullOrBlank() || endTime.isNullOrBlank()) return null

return "${formatToKoreanTime(context, startTime)} - ${formatToKoreanTime(context, endTime)}"
}

fun formatToKoreanTime(context: Context, time: String): String {
return try {
val parsedTime = LocalTime.parse(time)

val pattern = context.getString(R.string.time_format_base)
val formatter = DateTimeFormatter.ofPattern(pattern, Locale.KOREAN)
val base = parsedTime.format(formatter)

when (parsedTime.minute) {
0 -> base
30 -> context.getString(R.string.time_format_half_hour_with_base, base)
else -> context.getString(R.string.time_format_hour_minute_with_base, base, parsedTime.minute)
}
} catch (e: Exception) {
time
}
}

fun isPastDate(dateString: String, pattern: String = "yyyy-MM-dd"): Boolean {
return try {
val formatter = DateTimeFormatter.ofPattern(pattern)
val inputDate = LocalDate.parse(dateString, formatter)
inputDate.isBefore(LocalDate.now())
} catch (e: Exception) {
false
}
}
Comment on lines +43 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

isPastDate 함수의 예외 처리를 개선하세요.

여기서도 일반적인 Exception을 캐치하고 있으며, 로깅이 없습니다. 또한 실패 시 항상 false를 반환하는 것이 모든 상황에 적합한지 고려해 볼 필요가 있습니다.

다음과 같이 개선할 수 있습니다:

  fun isPastDate(dateString: String, pattern: String = "yyyy-MM-dd"): Boolean {
      return try {
          val formatter = DateTimeFormatter.ofPattern(pattern)
          val inputDate = LocalDate.parse(dateString, formatter)
          inputDate.isBefore(LocalDate.now())
-     } catch (e: Exception) {
+     } catch (e: DateTimeParseException) {
+         android.util.Log.e("DateTime", "Failed to parse date: $dateString with pattern: $pattern", e)
+         // 애플리케이션의 요구사항에 따라 기본값 결정
          false
+     } catch (e: IllegalArgumentException) {
+         android.util.Log.e("DateTime", "Invalid pattern: $pattern", e)
+         false
      }
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun isPastDate(dateString: String, pattern: String = "yyyy-MM-dd"): Boolean {
return try {
val formatter = DateTimeFormatter.ofPattern(pattern)
val inputDate = LocalDate.parse(dateString, formatter)
inputDate.isBefore(LocalDate.now())
} catch (e: Exception) {
false
}
}
fun isPastDate(dateString: String, pattern: String = "yyyy-MM-dd"): Boolean {
return try {
val formatter = DateTimeFormatter.ofPattern(pattern)
val inputDate = LocalDate.parse(dateString, formatter)
inputDate.isBefore(LocalDate.now())
} catch (e: DateTimeParseException) {
android.util.Log.e("DateTime", "Failed to parse date: $dateString with pattern: $pattern", e)
// 애플리케이션의 요구사항에 따라 기본값 결정
false
} catch (e: IllegalArgumentException) {
android.util.Log.e("DateTime", "Invalid pattern: $pattern", e)
false
}
}
🧰 Tools
🪛 detekt (1.23.7)

[warning] 57-57: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)


[warning] 57-57: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

Comment on lines +43 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C : 오전 오후를 표시하고 싶으면 a 패턴을 사용하면 간편하게 사용할 수 있습니다 ~

스크린샷 2025-04-27 오전 9 48 06

https://developer.android.com/reference/java/time/format/DateTimeFormatter

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다!

6 changes: 6 additions & 0 deletions core/ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@
<string name="attendance_status_absent">결석</string>
<string name="attendance_status_early_leave">조퇴</string>
<string name="attendance_status_excused">공결</string>

<string name="time_format_day">%1$d일</string>

<string name="time_format_base">a h시</string>
<string name="time_format_half_hour_with_base">%1$s 반</string>
<string name="time_format_hour_minute_with_base">%1$s %2$d분</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.yapp.feature.schedule

import com.yapp.model.ScheduleList
import java.time.LocalDate

data class ScheduleState(
val isLoading: Boolean = true,
val selectedTab: ScheduleTab = ScheduleTab.ALL,
val selectedYear: Int = LocalDate.now().year,
val selectedMonth: Int = LocalDate.now().monthValue,
val schedules: ScheduleList = ScheduleList(dates = emptyList())
)
Comment on lines +6 to +12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

ScheduleState에 에러 상태 추가 고려

현재 상태에는 isLoading만 있지만, 에러 처리를 위한 상태가 없습니다. 사용자에게 로딩 실패와 같은 에러 상태를 표시하기 위한 필드를 추가하는 것이 좋겠습니다.

data class ScheduleState(
    val isLoading: Boolean = true,
+   val error: String? = null,
    val selectedTab: ScheduleTab = ScheduleTab.ALL,
    val selectedYear: Int = LocalDate.now().year,
    val selectedMonth: Int = LocalDate.now().monthValue,
    val schedules: ScheduleList = ScheduleList(dates = emptyList())
)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data class ScheduleState(
val isLoading: Boolean = true,
val selectedTab: ScheduleTab = ScheduleTab.ALL,
val selectedYear: Int = LocalDate.now().year,
val selectedMonth: Int = LocalDate.now().monthValue,
val schedules: ScheduleList = ScheduleList(dates = emptyList())
)
data class ScheduleState(
val isLoading: Boolean = true,
val error: String? = null,
val selectedTab: ScheduleTab = ScheduleTab.ALL,
val selectedYear: Int = LocalDate.now().year,
val selectedMonth: Int = LocalDate.now().monthValue,
val schedules: ScheduleList = ScheduleList(dates = emptyList())
)


sealed interface ScheduleIntent {
data object EnterScheduleScreen: ScheduleIntent
data class SelectTab(val tab: ScheduleTab): ScheduleIntent
data object ClickPreviousMonth: ScheduleIntent
data object ClickNextMonth: ScheduleIntent
}
Comment on lines +14 to +19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

추가 Intent 고려: 에러 발생 시 재시도 기능

API 호출 실패 시 사용자가 재시도할 수 있는 Intent를 추가하면 좋을 것 같습니다.

sealed interface ScheduleIntent {
    data object EnterScheduleScreen: ScheduleIntent
    data class SelectTab(val tab: ScheduleTab): ScheduleIntent
    data object ClickPreviousMonth: ScheduleIntent
    data object ClickNextMonth: ScheduleIntent
+   data object RetryLoading: ScheduleIntent
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sealed interface ScheduleIntent {
data object EnterScheduleScreen: ScheduleIntent
data class SelectTab(val tab: ScheduleTab): ScheduleIntent
data object ClickPreviousMonth: ScheduleIntent
data object ClickNextMonth: ScheduleIntent
}
sealed interface ScheduleIntent {
data object EnterScheduleScreen: ScheduleIntent
data class SelectTab(val tab: ScheduleTab): ScheduleIntent
data object ClickPreviousMonth: ScheduleIntent
data object ClickNextMonth: ScheduleIntent
data object RetryLoading: ScheduleIntent
}


sealed interface ScheduleSideEffect {
data class ShowToast(val message: String): ScheduleSideEffect
}
Comment on lines +21 to +23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

다양한 SideEffect 추가 고려

현재는 토스트 메시지만 있지만, 화면 이동이나 다이얼로그 표시 같은 다른 사이드 이펙트도 필요할 수 있습니다.

sealed interface ScheduleSideEffect {
    data class ShowToast(val message: String): ScheduleSideEffect
+   data object NavigateBack: ScheduleSideEffect
+   data class ShowDialog(val title: String, val message: String): ScheduleSideEffect
}

Committable suggestion skipped: line range outside the PR's diff.


enum class ScheduleTab(val index: Int, val labelResId: Int) {
ALL(0, R.string.schedule_tab_all),
SESSION(1, R.string.schedule_tab_session);
}
Loading