Skip to content
Open
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
58 changes: 57 additions & 1 deletion app/src/main/java/team/retum/jobisandroidv2/JobisNavigator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,26 +130,55 @@ internal class JobisNavigator(
navController.navigateToNotices()
}

/**
* Navigates to the landing screen using the provided pop-up route.
*
* @param popUpRoute The navigation route to pop up to when navigating to the landing screen.
*/
fun navigateToLanding(popUpRoute: String) {
navController.navigateToLanding(popUpRoute = popUpRoute)
}

/**
* Navigate to the post-review screen for a specific company.
*
* @param companyName The company's display name to prefill or show on the post-review screen.
* @param companyId The unique identifier of the company to associate the review with.
*/
fun navigateToPostReview(companyName: String, companyId: Long) {
navController.navigateToPostReview(companyName = companyName, companyId = companyId)
}

/**
* Navigates to the post-next-review screen, forwarding the given review data.
*
* @param reviewData The `PostReviewData` to pass to the destination.
*/
fun navigateToPostNextReview(reviewData: PostReviewData) {
navController.navigateToPostNextReview(reviewData = reviewData)
}

/**
* Navigates to the post-review completion screen.
*/
fun navigateToPostReviewComplete() {
navController.navigateToPostReviewComplete()
}

/**
* Navigate to the post-expected-review screen with the provided review data.
*
* @param reviewData Data used to populate the post-expected-review screen.
*/
fun navigateToPostExpectReview(reviewData: PostReviewData) {
navController.navigateToPostExpectReview(reviewData = reviewData)
}

/**
* Navigates to the app's root (landing/home) screen, optionally targeting a specific application.
*
* @param applicationId The application ID to include in the navigation target; pass 0 to navigate to the root without targeting a specific application.
*/
fun navigateToRoot(applicationId: Long = 0) {
navController.navigateToRoot(applicationId = applicationId)
}
Expand Down Expand Up @@ -194,29 +223,56 @@ internal class JobisNavigator(
navController.navigateToSearchCompanies()
}

/**
* Navigate to the notice details screen for the specified notice.
*
* @param noticeId The identifier of the notice to display.
*/
fun navigateToNoticeDetails(noticeId: Long) {
navController.navigateToNoticeDetails(noticeId = noticeId)
}

/**
* Navigates to the review details screen for the specified review.
*
* @param reviewId The identifier of the review to display.
*/
fun navigateToReviewDetails(reviewId: Long) {
navController.navigateToReviewDetails(reviewId = reviewId)
}

/**
* Navigates to the review search screen.
*/
fun navigateToSearchReview() {
navController.navigateToSearchReview()
}

/**
* Navigates to the review filter screen.
*/
fun navigateToReviewFilter() {
navController.navigateToReviewFilter()
}

/**
* Navigates to the review screen for the given company.
*
* @param companyId The company's unique identifier.
* @param companyName The company's display name.
*/
fun navigateToReview(
companyId: Long,
companyName: String,
) {
navController.navigateToReview()
}

/**
* Determine whether navigation originated from the notifications screen.
*
* @return `true` if the previous back-stack entry's route equals `NAVIGATION_NOTIFICATIONS`, `false` otherwise.
*/
fun navigatedFromNotifications(): Boolean {
return navController.previousBackStackEntry?.destination?.route == NAVIGATION_NOTIFICATIONS
}
Expand All @@ -241,4 +297,4 @@ internal fun rememberJobisNavigator(
navController: NavHostController = rememberNavController(),
): JobisNavigator = remember(navController) {
JobisNavigator(navController)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ import team.retum.review.navigation.searchReview

const val NAVIGATION_MAIN = "main"

/**
* Registers the app's main navigation graph and wires each destination's navigation callbacks to the provided navigator.
*
* Creates a navigation graph with route `NAVIGATION_MAIN` and start destination `NAVIGATION_ROOT`, adding all app
* destinations (root, notifications, recruitment, companies, reviews, posts, etc.) and connecting their navigation
* actions to the given `JobisNavigator`.
*/
internal fun NavGraphBuilder.mainNavigation(
navigator: JobisNavigator,
) {
Expand Down Expand Up @@ -138,4 +145,4 @@ internal fun NavGraphBuilder.mainNavigation(
onReviewDetailClick = navigator::navigateToReviewDetails,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import team.retum.common.model.ApplicationData
const val NAVIGATION_ROOT = "root"
const val APPLICATION_ID = "applicationId"

/**
* Adds the root navigation destination and wires UI callbacks into the Root composable.
*
* This defines the route "$NAVIGATION_ROOT{$APPLICATION_ID}" with a String `APPLICATION_ID` argument,
* parses that argument to a Long and forwards it as `applicationId` to Root along with all provided callbacks.
*
* @param navigatedFromNotifications True when navigation originated from a notification; forwarded to Root.
*/
fun NavGraphBuilder.root(
onAlarmClick: () -> Unit,
onEmploymentClick: () -> Unit,
Expand Down Expand Up @@ -66,4 +74,4 @@ fun NavController.navigateToRoot(applicationId: Long = 0) {
navigate(NAVIGATION_ROOT + applicationId) {
popUpTo(0)
}
}
}
27 changes: 26 additions & 1 deletion app/src/main/java/team/retum/jobisandroidv2/root/RootScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ import team.retum.jobisdesignsystemv2.foundation.JobisTypography
import team.retum.jobisdesignsystemv2.text.JobisText
import team.retum.review.navigation.review

/**
* Hosts the app's root UI, wires navigation and action callbacks, and controls the rejection modal state
* forwarded to RootScreen.
*
* @param applicationId Optional application ID to preselect when entering the app.
* @param navigateToLanding Navigates to the landing (auth/entry) screen.
* @param navigateToApplication Navigates to the application flow with the provided ApplicationData.
* @param navigateToRecruitmentDetails Navigates to the recruitment details screen for the given recruitment ID.
* @param navigatedFromNotifications `true` when the current navigation was initiated from a notification; forwarded to Home.
*/
@Composable
internal fun Root(
applicationId: Long?,
Expand Down Expand Up @@ -111,6 +121,21 @@ internal fun Root(
)
}

/**
* Composes the app's main navigation scaffold with a bottom navigation bar and a modal rejection bottom sheet.
*
* Hosts the navigation graph (Home, Recruitments, Bookmarks, Review, MyPage), wires screen-level callbacks into each destination,
* and displays a RejectionBottomSheet using the provided sheet state and rejection reason.
*
* @param sheetState Controls visibility and state of the modal bottom sheet shown for rejection details.
* @param applicationId If non-null, the Home destination will receive this application id to display related content.
* @param showRejectionModal Callback invoked with ApplicationData to open the rejection bottom sheet populated for that application.
* @param rejectionReason Text shown inside the rejection bottom sheet.
* @param navigateToApplicationByRejectionBottomSheet Callback invoked when the user chooses to re-apply from the rejection bottom sheet.
* @param navigateToApplication Callback used to navigate to an application detail screen with the provided ApplicationData.
* @param navigateToRecruitmentDetails Callback used to navigate to a recruitment details screen with the provided recruitment id.
* @param navigatedFromNotifications Indicates whether navigation to Home originated from a notifications entry point (affects Home destination behavior).
*/
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
private fun RootScreen(
Expand Down Expand Up @@ -234,4 +259,4 @@ private fun RejectionBottomSheet(
onClick = onReApplyClick,
color = ButtonColor.Primary,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,25 @@ import team.retum.network.model.response.FetchReviewsCountResponse
import team.retum.network.model.response.FetchReviewsResponse

interface ReviewRepository {
suspend fun postReview(reviewRequest: PostReviewRequest)
/**
* Persists a new review using the provided review request data.
*
* @param reviewRequest The request payload containing the review details to save.
*/
suspend fun postReview(reviewRequest: PostReviewRequest)

/**
* Retrieves a page of reviews applying optional filters.
*
* @param page The page number to retrieve; null to use default paging.
* @param location Filter by interview location; null to include all locations.
* @param interviewType Filter by interview type; null to include all types.
* @param keyword Full- or partial-text search term; null to disable keyword filtering.
* @param year Filter reviews by year; null to include all years.
* @param companyId Filter reviews for a specific company id; null to include all companies.
* @param code Additional numeric filter for review classification; null to ignore.
* @return A FetchReviewsResponse containing the matching reviews and related pagination metadata.
*/
suspend fun fetchReviews(
page: Int?,
location: InterviewLocation?,
Expand All @@ -22,11 +39,32 @@ interface ReviewRepository {
code: Long?,
): FetchReviewsResponse

suspend fun fetchReviewDetail(reviewId: String): FetchReviewDetailResponse
/**
* Retrieves detailed information for a specific review.
*
* @param reviewId The unique identifier of the review to retrieve.
* @return A FetchReviewDetailResponse containing the review's full details.
*/
suspend fun fetchReviewDetail(reviewId: String): FetchReviewDetailResponse

suspend fun fetchQuestions(): FetchQuestionsResponse
/**
* Retrieves the list of interview questions associated with reviews.
*
* @return A FetchQuestionsResponse containing the available questions and related metadata.
*/
suspend fun fetchQuestions(): FetchQuestionsResponse

suspend fun fetchReviewsCount(): FetchReviewsCountResponse
/**
* Retrieves aggregate counts for reviews.
*
* @return FetchReviewsCountResponse containing the total number of reviews and any available breakdowns by criteria.
*/
suspend fun fetchReviewsCount(): FetchReviewsCountResponse

suspend fun fetchMyReviews(): FetchMyReviewResponse
}
/**
* Retrieves reviews authored by the current authenticated user.
*
* @return `FetchMyReviewResponse` containing the current user's reviews and associated response metadata.
*/
suspend fun fetchMyReviews(): FetchMyReviewResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,28 @@ import javax.inject.Inject
class ReviewRepositoryImpl @Inject constructor(
private val reviewDataSource: ReviewDataSource,
) : ReviewRepository {
override suspend fun postReview(reviewRequest: PostReviewRequest) =
/**
* Submits a review to the data source.
*
* @param reviewRequest The review payload to submit.
* @return The response returned by the data source after posting the review.
*/
override suspend fun postReview(reviewRequest: PostReviewRequest) =
reviewDataSource.postReview(reviewRequest)

override suspend fun fetchReviews(
/**
* Fetches a paginated list of reviews matching the provided filters.
*
* @param page The page number to retrieve, or `null` to use the default first page.
* @param location Filter by interview location, or `null` to include all locations.
* @param interviewType Filter by interview type, or `null` to include all types.
* @param keyword Filter reviews by keyword contained in their content, or `null` for no keyword filtering.
* @param year Filter reviews by the interview year, or `null` to include all years.
* @param companyId Filter reviews for a specific company ID, or `null` to include all companies.
* @param code Filter reviews by a specific code, or `null` to include all codes.
* @return A FetchReviewsResponse containing the matching reviews and pagination metadata.
*/
override suspend fun fetchReviews(
page: Int?,
location: InterviewLocation?,
interviewType: InterviewType?,
Expand All @@ -36,15 +54,36 @@ class ReviewRepositoryImpl @Inject constructor(
code = code,
)

override suspend fun fetchReviewDetail(reviewId: String): FetchReviewDetailResponse =
/**
* Retrieves detailed information for a specific review.
*
* @param reviewId The identifier of the review to retrieve.
* @return A FetchReviewDetailResponse containing the review's detailed data.
*/
override suspend fun fetchReviewDetail(reviewId: String): FetchReviewDetailResponse =
reviewDataSource.fetchReviewDetail(reviewId)

override suspend fun fetchQuestions(): FetchQuestionsResponse =
/**
* Retrieves the predefined review questions used for submitting or displaying reviews.
*
* @return A [FetchQuestionsResponse] containing the list of questions and any related metadata.
*/
override suspend fun fetchQuestions(): FetchQuestionsResponse =
reviewDataSource.fetchQuestions()

override suspend fun fetchReviewsCount(): FetchReviewsCountResponse =
/**
* Retrieves a summary of review counts.
*
* @return A FetchReviewsCountResponse containing the total number of reviews and any related count breakdowns.
*/
override suspend fun fetchReviewsCount(): FetchReviewsCountResponse =
reviewDataSource.fetchReviewsCount()

override suspend fun fetchMyReviews(): FetchMyReviewResponse =
/**
* Retrieves the authenticated user's submitted reviews.
*
* @return A [FetchMyReviewResponse] containing the user's reviews and any associated metadata.
*/
override suspend fun fetchMyReviews(): FetchMyReviewResponse =
reviewDataSource.fetchMyReviews()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ import team.retum.jobisdesignsystemv2.foundation.JobisTheme
import team.retum.jobisdesignsystemv2.foundation.JobisTypography
import team.retum.jobisdesignsystemv2.text.JobisText

/**
* Displays a tappable card showing a review's author and year.
*
* @param onClick Callback invoked with [reviewId] when the card is clicked.
* @param reviewId Identifier for the review that will be passed to [onClick].
* @param writer Text displayed as the review's author.
* @param year Text displayed as the review's year.
*/
@Composable
fun ReviewContent(
onClick: (Long) -> Unit,
Expand Down Expand Up @@ -51,4 +59,4 @@ fun ReviewContent(
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ data class FetchReviewDetailEntity(
)
}

/**
* Converts a FetchReviewDetailResponse into a FetchReviewDetailEntity.
*
* @return A FetchReviewDetailEntity with metadata fields copied from the response and `qnaResponse` transformed into a list of entity `QnAs`.
*/
internal fun FetchReviewDetailResponse.toEntity() = FetchReviewDetailEntity(
reviewId = this.reviewId,
companyName = this.companyName,
Expand All @@ -38,8 +43,13 @@ internal fun FetchReviewDetailResponse.toEntity() = FetchReviewDetailEntity(
answer = this.answer,
)

/**
* Convert a response QnA model into its entity representation.
*
* @return A FetchReviewDetailEntity.QnAs containing the same `id`, `question`, and `answer` as the receiver.
*/
private fun FetchReviewDetailResponse.QnAs.toEntity() = FetchReviewDetailEntity.QnAs(
id = this.id,
question = this.question,
answer = this.answer,
)
)
Loading