Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7f8235c
[mod/#216] profileImageUrl 필수인자
oilbeaneda Jul 16, 2026
dc1eaef
[fix/#216] 매칭 미시작 강사 홈 진입 시 409 토스트 제거
oilbeaneda Jul 16, 2026
678f579
[fix/#216] 강사 홈 init 최초 로드 제거 (ON_RESUME으로 이관)
oilbeaneda Jul 16, 2026
fdf56e1
[fix/#216] 강사 홈 ON_RESUME마다 홈 재조회 (매칭 취소 후 카드 동기화)
oilbeaneda Jul 16, 2026
9e0b895
[fix/#204] 즉시매칭 시작 시 조건 설정(I05) 건너뛰고 이전 조건으로 매칭되는 문제 수정
oilbeaneda Jul 16, 2026
318b502
[fix/#204] 매칭 상세 정산 금액 필드 정렬 및 종료 offer 처리
oilbeaneda Jul 16, 2026
077a5a4
[fix/#204] 확정/진행 강습 보유 시 홈 즉시매칭 시작 차단
oilbeaneda Jul 16, 2026
755a2ae
[fix/#216] NavigateToMatchingWaiting에 offerId 추가
oilbeaneda Jul 16, 2026
a1603d6
[fix/#216] 홈 카드 클릭 분기를 lessonId/offerId 기반으로 변경
oilbeaneda Jul 16, 2026
fc72bf7
[fix/#216] NavigateToMatchingWaiting offerId 전달
oilbeaneda Jul 16, 2026
d08e633
[fix/#216] navigateToMatchingWaiting 시그니처 (Long?) -> Unit 변경
oilbeaneda Jul 16, 2026
17cac0f
[fix/#216] 매칭 카드 클릭 시 offerId를 InstructorMatching 라우트에 전달
oilbeaneda Jul 16, 2026
67edaeb
[fix/#204] 조건 수정 진입 시 기존 노출 일시중지 (수정 중 요청 유입 차단)
oilbeaneda Jul 16, 2026
3d3327c
[fix/#204] 강습 상세 뒤로가기 홈 복귀 (매칭 스택 정리)
oilbeaneda Jul 16, 2026
78dae66
[fix/#204] 매칭 offer 종료/거절 시 활성 상태 재조회로 동기화
oilbeaneda Jul 16, 2026
f2aa6fa
[fix/#204] 확정 전 매칭 재진입 시 대기화면 오노출 수정 (resync는 Waiting에서만)
oilbeaneda Jul 16, 2026
537aa49
[fix/#204] 매칭 대기 화면 전환 정리
oilbeaneda Jul 16, 2026
5488e5f
[merge/#216] pulled develop
oilbeaneda Jul 16, 2026
9826212
[merge/#216] pulled develop
oilbeaneda Jul 16, 2026
0b5e7df
[merge/#216] pulled develop
oilbeaneda Jul 17, 2026
77ba598
[mod/#216] 매칭 완료되면 홈에 강습 상세 보기가 떠야함
oilbeaneda Jul 17, 2026
4584e70
[mod/#216] navOptions 보강
oilbeaneda Jul 17, 2026
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
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.navOptions
import com.ssing.core.ui.extension.clearBackStackNavOptions
import com.ssing.core.ui.navigation.Route
import com.ssing.core.ui.navigation.SsingNavHost
Expand Down Expand Up @@ -55,17 +56,20 @@ internal fun InstructorMainNavHost(
paddingValues = paddingValues,
navigateToMatching = {
navController.navigate(
route = InstructorMatching(),
route = InstructorMatching(startFresh = true),
navOptions = navController.backToHomeNavOptions(),
)
},
navigateToLessonDetail = { lessonId ->
navController.navigate(
route = InstructorLesson(lessonId = lessonId ?: 0),
navOptions = navController.backToHomeNavOptions(),
)
},
navigateToMatchingWaiting = {
navigateToMatchingWaiting = { offerId ->
navController.navigate(
route = InstructorMatching
route = InstructorMatching(offerId = offerId),
navOptions = navController.backToHomeNavOptions(),
)
},
navigateToNotification = {
Expand All @@ -82,14 +86,18 @@ internal fun InstructorMainNavHost(
navigateToLessonDetail = { lessonId ->
navController.navigate(
route = InstructorLesson(lessonId = lessonId),
navOptions = navController.backToHomeNavOptions(),
)
},
)
instructorLessonNavGraph(
paddingValues = paddingValues,
navController = navController,
navigateToMatching = {
navController.navigate(route = InstructorMatching())
navController.navigate(
route = InstructorMatching(),
navOptions = navController.backToHomeNavOptions(),
)
},
navigateToHome = {
navController.navigate(
Expand All @@ -109,3 +117,7 @@ internal fun InstructorMainNavHost(
)
}
}
private fun NavHostController.backToHomeNavOptions() = navOptions {
popUpTo(InstructorHome) { inclusive = false }
launchSingleTop = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ data class InstructorProfileResponse(
@SerialName("gender") val gender: String,
@SerialName("birthYear") val birthYear: Int,
@SerialName("level") val level: Int,
@SerialName("profileImageUrl") val profileImageUrl: String? = null,
@SerialName("profileImageUrl") val profileImageUrl: String,
)

@Serializable
Expand Down Expand Up @@ -156,4 +156,4 @@ data class ConsumerMatchingRequestResponse(
@SerialName("representativeMemberName") val representativeMemberName: String,
@SerialName("headcount") val headcount: Int,
@SerialName("participants") val participants: List<ParticipantResponse>,
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ internal class ConsumerLessonRepositoryImpl @Inject constructor(
gender = gender,
birthYear = birthYear,
level = level,
profileImageUrl = profileImageUrl.orEmpty(),
profileImageUrl = profileImageUrl,
)

private fun ParticipantResponse.toModel() = LessonParticipant(participantId, gender, age)
Expand Down Expand Up @@ -176,4 +176,4 @@ internal class ConsumerLessonRepositoryImpl @Inject constructor(
)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,5 @@ data class InstructorMatchingLessonSummary(
)

data class InstructorMatchingPriceSummary(
val lessonPriceAmount: Int,
val resortPassFeeAmount: Int,
val totalPaymentAmount: Int,
val instructorSettlementAmount: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,5 @@ internal data class InstructorMatchingLessonSummaryResponse(

@Serializable
internal data class InstructorMatchingPriceSummaryResponse(
@SerialName("lessonPriceAmount") val lessonPriceAmount: Int,
@SerialName("resortPassFeeAmount") val resortPassFeeAmount: Int,
@SerialName("totalPaymentAmount") val totalPaymentAmount: Int,
@SerialName("instructorSettlementAmount") val instructorSettlementAmount: Int? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,7 @@ internal class InstructorMatchingRepositoryImpl @Inject constructor(
},
priceSummary = requireNotNull(priceSummary) { "AVAILABLE 응답에 priceSummary 누락" }.let {
InstructorMatchingPriceSummary(
lessonPriceAmount = it.lessonPriceAmount,
resortPassFeeAmount = it.resortPassFeeAmount,
totalPaymentAmount = it.totalPaymentAmount,
instructorSettlementAmount = it.instructorSettlementAmount ?: 0,
)
},
participants = participants.map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal interface InstructorHomeContract {
val averageRating: Float = 0f,
val grade: Grade = Grade.GRADE1,
val achievementRate: Int = 0,
val hasActiveLesson: Boolean = false,
)

sealed interface Effect {
Expand All @@ -29,7 +30,7 @@ internal interface InstructorHomeContract {
val offerId: Long? = null,
) : Effect

data object NavigateToMatchingWaiting : Effect
data class NavigateToMatchingWaiting(val offerId: Long? = null) : Effect
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.ssing.core.ui.R
import com.ssing.core.ui.common.component.Empty
Expand All @@ -48,16 +50,23 @@ internal fun InstructorHomeRoute(
navigateToLessonDetail: (Long?) -> Unit,
navigateToMatching: () -> Unit,
navigateToNotification: () -> Unit,
navigateToMatchingWaiting: (Long?) -> Unit,
modifier: Modifier = Modifier,
viewModel: InstructorHomeViewModel = hiltViewModel(),
navigateToMatchingWaiting: () -> Unit,
) {
val state by viewModel.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current

LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
viewModel.loadHome()
}

HandleUiEffects(viewModel.uiEffect) { effect ->
when (effect) {
is InstructorHomeContract.Effect.NavigateToMatchingWaiting -> navigateToMatchingWaiting
is InstructorHomeContract.Effect.NavigateToMatchingWaiting -> navigateToMatchingWaiting(
effect.offerId
)

is InstructorHomeContract.Effect.NavigateToLessonDetail -> navigateToLessonDetail(effect.lessonId)
is InstructorHomeContract.Effect.NavigateToMatching -> navigateToMatching()
is InstructorHomeContract.Effect.ShowToast -> context.toast(effect.message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ internal class InstructorHomeViewModel @Inject constructor(
InstructorHomeContract.State()
) {

init {
loadHome()
}

fun loadHome() {
viewModelScope.launch {
updateState {
Expand All @@ -47,6 +43,9 @@ internal class InstructorHomeViewModel @Inject constructor(
copy(
isLoading = false,
lessonCards = result.lessonCards.toUiState(),
hasActiveLesson = result.lessonCards.any {
it.displayStatus == CONFIRMED || it.displayStatus == IN_PROGRESS
},
hasUnreadNotification = result.hasUnreadNotification,
instructorName = result.instructorName,
matchingPeopleCount = result.matchingPeopleCount,
Expand Down Expand Up @@ -111,10 +110,9 @@ internal class InstructorHomeViewModel @Inject constructor(

WAITING_FOR_INSTRUCTOR,
WAITING_FOR_CONFIRMATION,
PAYMENT_PENDING,
IN_PROGRESS -> Status.Matched

CONFIRMED -> Status.Default
PAYMENT_PENDING -> Status.Matched
CONFIRMED,
IN_PROGRESS -> Status.Default

else -> Status.Default
}
Expand All @@ -126,58 +124,33 @@ internal class InstructorHomeViewModel @Inject constructor(
const val PAYMENT_PENDING = "PAYMENT_PENDING"
const val CONFIRMED = "CONFIRMED"
const val IN_PROGRESS = "IN_PROGRESS"

const val MSG_ACTIVE_LESSON_BLOCK =
"진행 예정이거나 진행 중인 강습이 있어 즉시매칭을 시작할 수 없어요."
}

fun onMatchingClick() {
// 확정/진행 중인 강습이 있으면 즉시매칭 시작을 막는다.
if (uiState.value.hasActiveLesson) {
sendEffect(InstructorHomeContract.Effect.ShowToast(MSG_ACTIVE_LESSON_BLOCK))
return
}
sendEffect(InstructorHomeContract.Effect.NavigateToMatching)
}

fun onLessonClick(
lesson: Reservation,
) {
Timber.i("lessonId: ${lesson.lessonId} / offerId: $lesson.offerId")

when (lesson.status) {
Status.Matching -> {
sendEffect(
InstructorHomeContract.Effect.NavigateToMatching
)
}

Status.Matched -> {
val lessonId = lesson.lessonId

if (lessonId == null) {
sendEffect(InstructorHomeContract.Effect.NavigateToMatching)
return
}

sendEffect(
InstructorHomeContract.Effect.NavigateToLessonDetail(
lessonId = lessonId,
)
)
}

Status.Default -> {
val lessonId = lesson.lessonId
val offerId = lesson.offerId

if (lessonId == null || offerId == null) {
Timber.e(
"강습 상세 이동에 필요한 lessonId나 offerId가 없습니다. " +
"status=${lesson.status}, offerId=${lesson.offerId}"
)
return
}

sendEffect(
InstructorHomeContract.Effect.NavigateToLessonDetail(
lessonId = lessonId,
offerId = offerId,
)
)
}
fun onLessonClick(lesson: Reservation) {
Timber.i("lessonId: ${lesson.lessonId} / offerId: ${lesson.offerId}")

when {
lesson.lessonId != null -> sendEffect(
InstructorHomeContract.Effect.NavigateToLessonDetail(lessonId = lesson.lessonId)
)
lesson.offerId != null -> sendEffect(
InstructorHomeContract.Effect.NavigateToMatchingWaiting(offerId = lesson.offerId)
)
else -> sendEffect(
InstructorHomeContract.Effect.NavigateToMatchingWaiting(offerId = null)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fun NavGraphBuilder.instructorHomeNavGraph(
navigateToMatching: () -> Unit,
navigateToNotification: () -> Unit,
navigateToLessonDetail: (Long?) -> Unit,
navigateToMatchingWaiting: () -> Unit,
navigateToMatchingWaiting: (Long?) -> Unit,
) {
composable<InstructorHome> {
InstructorHomeRoute(
Expand Down
Loading
Loading