Skip to content

Conversation

@vvan2
Copy link
Member

@vvan2 vvan2 commented Nov 5, 2025

ISSUE

❗ WORK DESCRIPTION

  • Icon 추가 및 navigation 수정
  • 공통 컴포넌트 생성(Actionbutton) 및 수정
  • 온보딩 화면 (단계별 화면 분리)
  • 온보딩 메인 UI 작업

📸 SCREENSHOT

-.Clipchamp.6.mp4

@vvan2 vvan2 requested a review from dmp100 November 5, 2025 11:12
@vvan2 vvan2 self-assigned this Nov 5, 2025
@vvan2 vvan2 added feature ✨ 기능 구현 주완🐹 주완 전용 라벨 labels Nov 5, 2025
@vvan2 vvan2 linked an issue Nov 5, 2025 that may be closed by this pull request
1 task
@coderabbitai
Copy link

coderabbitai bot commented Nov 5, 2025

Walkthrough

Introduces comprehensive onboarding flow with nine-step UI screens, state management via OnboardingViewModel, and navigation integration into the main app. Adds supporting UI components including HsLinkActionButton and OnboardingProgressBar, updates HsLinkTextField with keyboard configuration options, and provides drawable resources. New navigation structure enables home navigation from onboarding completion.

Changes

Cohort / File(s) Summary
Design System Components
HsLinkActionButton.kt, HsLinkTextField.kt
Introduces HsLinkActionButton with three sizes (Small, Medium, Large) and text style selection. Updates HsLinkTextField to accept configurable KeyboardOptions parameter and adjusts placeholder text style to caption_14Normal.
Navigation Setup
MainNavHost.kt, MainNavigator.kt, OnboardingNavigation.kt
Integrates onboarding navigation graph into MainNavHost. Changes MainNavigator start destination from Home to Onboarding. Adds navigateToHome callback and navigateToOnboarding extension function. Creates serializable Onboarding route.
Onboarding Screens
OnboardingScreen.kt, StudentIdScreen.kt, NameScreen.kt, MajorScreen.kt, EmploymentStatusScreen.kt, CareerScreen.kt, JobInfoScreen.kt, JobSeekingScreen.kt, MentorshipScreen.kt, LinkScreen.kt
Implements ten-component onboarding UI with reusable OnboardingScreen layout, nine step-specific screens with progressively collecting user data (student ID, name, major, employment status, career details, job info, job seeking status, mentorship preference, external links), and validation logic enabling navigation based on field completion.
Onboarding Components
OnboardingProgressBar.kt, AddLinkDialog.kt
Adds visual progress indicator showing completion percentage. Introduces dialog for adding external links with type selection and URL input.
State Management
OnboardingViewModel.kt
Implements Hilt-injected ViewModel managing onboarding flow state with StateFlow, providing update methods for all user inputs, step navigation (previous/next), and link management. Includes submitOnboarding placeholder for repository integration.
Data Models
OnboardingState.kt, MajorData.kt
Defines OnboardingState data class with student/employment/link fields and progress tracking. Establishes enums for OnboardingStep (with computed progress), EmploymentStatus, JobType, LinkType with localized labels. Provides static MajorData list.
UI Resources
drawable/ic_onboarding_plus.xml, drawable/ic_onboarding_search.xml
Adds vector drawable icons for onboarding UI (plus icon 14dp, search icon 24dp).

Sequence Diagram

sequenceDiagram
    participant User
    participant OnboardingRoute
    participant OnboardingViewModel
    participant Screen as Step Screen
    participant NavController

    User->>NavController: Navigate to Onboarding
    NavController->>OnboardingRoute: Render route
    OnboardingRoute->>OnboardingViewModel: Collect state
    OnboardingViewModel-->>OnboardingRoute: Emit OnboardingState
    
    rect rgba(100, 150, 255, 0.2)
    Note over User,Screen: Step Loop (1-9)
    User->>Screen: Interact with current step
    Screen->>OnboardingViewModel: updateField(value)
    OnboardingViewModel->>OnboardingViewModel: Mutate state
    OnboardingViewModel-->>Screen: Emit updated state
    Screen-->>User: Re-render with new data
    end
    
    User->>Screen: Click Next/Previous
    Screen->>OnboardingViewModel: moveToNextStep() / moveToPreviousStep()
    OnboardingViewModel->>OnboardingViewModel: Advance currentStep
    OnboardingViewModel-->>OnboardingRoute: Emit new step
    OnboardingRoute->>Screen: Render next screen
    
    User->>Screen: Complete final step
    Screen->>OnboardingViewModel: submitOnboarding()
    OnboardingViewModel->>NavController: navigateHome()
    NavController-->>User: Navigate to Home
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Multiple new screen implementations: Nine onboarding screens with varying complexity, each with state management and validation logic that requires individual review
  • State management layer: OnboardingViewModel with 15+ update methods and navigation logic; ensure proper coroutine handling and state mutation patterns
  • Navigation integration: Changes to MainNavHost and MainNavigator alter app startup flow; verify navigateHome callback is correctly wired and navigation graph is properly composed
  • Data model design: Review OnboardingState structure for completeness and enum definitions for proper localization support
  • UI component composition: Verify HsLinkActionButton sizing logic and HsLinkTextField keyboard options integration don't break existing usages

Possibly related PRs

  • Hsu-Connect/Connect-Front#2: Initial project setup providing foundation code (Modifier.noRippleClickable, MainNavHost/MainNavigator structure) upon which this onboarding feature builds and integrates

Suggested reviewers

  • dmp100

Poem

🐰 Through nine steps hops the rabbit swift,
Onboarding's dance—a feature gift!
States flow smooth as carrots arranged,
Nav routes new, progressions changed.

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title '[feat/#16] Onboarding UI' directly and clearly summarizes the main change: implementing the onboarding UI feature, matching the linked issue and PR objectives.
Description check ✅ Passed The PR description follows the repository template with ISSUE, WORK DESCRIPTION, SCREENSHOT, and TO REVIEWERS sections. It includes issue reference, clear work items, video evidence, and helpful notes for reviewers.
Linked Issues check ✅ Passed The PR implements the onboarding UI feature required by issue #16, including all screen components, navigation logic, view model, and supporting UI elements as intended.
Out of Scope Changes check ✅ Passed All changes directly support the onboarding UI objective: new onboarding screens, navigation integration, shared components (ActionButton), model classes, and required icons are all within scope.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#16-onboarding-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vvan2 vvan2 changed the title [Feat/#16] Onboarding UI [feat/#16] Onboarding UI Nov 5, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (7)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/JobSeekingScreen.kt (1)

47-63: Remove redundant isEnabled = true parameter.

Both HsLinkSelectButton instances explicitly pass isEnabled = true, which is the default value. This can be omitted for cleaner code.

Apply this diff:

 HsLinkSelectButton(
     label = "구직 중이에요",
     onClick = { onJobSeekingSelect(true) },
     size = HsLinkButtonSize.Large,
     modifier = Modifier.fillMaxWidth(),
-    isEnabled = true,
     isSelected = isJobSeeking == true
 )

 HsLinkSelectButton(
     label = "구직하지 않아요",
     onClick = { onJobSeekingSelect(false) },
     size = HsLinkButtonSize.Large,
     modifier = Modifier.fillMaxWidth(),
-    isEnabled = true,
     isSelected = isJobSeeking == false
 )
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/OnboardingProgressBar.kt (1)

15-33: Consider clamping the progress value.

The progress parameter is not validated. While Compose's fillMaxWidth handles out-of-range values gracefully, explicit validation would make the contract clearer and catch bugs in calling code earlier.

Apply this diff:

 @Composable
 fun OnboardingProgressBar(
     progress: Float,
     modifier: Modifier = Modifier
 ) {
+    val clampedProgress = progress.coerceIn(0f, 1f)
+    
     Box(
         modifier = modifier
             .fillMaxWidth()
             .height(4.dp)
             .clip(RoundedCornerShape(2.dp))
             .background(HsLinkTheme.colors.Grey200)
     ) {
         Box(
             modifier = Modifier
-                .fillMaxWidth(progress)
+                .fillMaxWidth(clampedProgress)
                 .height(4.dp)
                 .clip(RoundedCornerShape(2.dp))
                 .background(HsLinkTheme.colors.SkyBlue500)
         )
     }
 }
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/MentorshipScreen.kt (1)

47-63: Remove redundant isEnabled = true parameter.

Both HsLinkSelectButton instances explicitly pass isEnabled = true, which is the default value and can be omitted.

Apply this diff:

 HsLinkSelectButton(
     label = "멘토로도 참여할래요",
     onClick = { onMentorshipSelect(true) },
     size = HsLinkButtonSize.Large,
     modifier = Modifier.fillMaxWidth(),
-    isEnabled = true,
     isSelected = wantsMentorship == true
 )

 HsLinkSelectButton(
     label = "멘티로만 참여할래요",
     onClick = { onMentorshipSelect(false) },
     size = HsLinkButtonSize.Large,
     modifier = Modifier.fillMaxWidth(),
-    isEnabled = true,
     isSelected = wantsMentorship == false
 )
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/EmploymentStatusScreen.kt (1)

26-26: Remove unnecessary @OptIn annotation.

The @OptIn(ExperimentalLayoutApi::class) annotation is present but FlowRow is not used in this file—only the stable Row API is used. This annotation can be removed.

Apply this diff:

-@OptIn(ExperimentalLayoutApi::class)
 @Composable
 fun EmploymentStatusScreen(
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/AddLinkDialog.kt (1)

58-58: Improve Korean text clarity.

The description text is grammatically awkward. Consider revising to "나눔 활동에 참여할 때 기본 정보로 활용됩니다." for better clarity.

Apply this diff:

-                text = "나눔에 관련을 할 때 기본 정보로 활용됩니다.",
+                text = "나눔 활동에 참여할 때 기본 정보로 활용됩니다.",
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/MajorScreen.kt (1)

110-128: Consider using an icon for the clear button.

The clear button currently uses the text character "✕" (Line 122). For better semantics and consistency with the design system, consider using a proper icon resource like R.drawable.ic_close or similar.

app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/JobInfoScreen.kt (1)

185-221: Remove redundant isEnabled parameters.

All JobType selection buttons have isEnabled = true explicitly set, which is redundant since true is the default value for the isEnabled parameter in HsLinkSelectButton.

Apply this diff to remove the redundant parameters:

                         HsLinkSelectButton(
                             modifier = Modifier.weight(1f),
                             label = JobType.FULL_TIME.label,
                             onClick = { onJobTypeSelect(JobType.FULL_TIME) },
                             size = HsLinkButtonSize.Large,
-                            isEnabled = true,
                             isSelected = selectedJobType == JobType.FULL_TIME
                         )
                         HsLinkSelectButton(
                             modifier = Modifier.weight(1f),
                             label = JobType.CONTRACT.label,
                             onClick = { onJobTypeSelect(JobType.CONTRACT) },
                             size = HsLinkButtonSize.Large,
-                            isEnabled = true,
                             isSelected = selectedJobType == JobType.CONTRACT
                         )

(Apply the same change to INTERN and FREELANCER buttons)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5819b31 and a4e6f28.

📒 Files selected for processing (22)
  • app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkActionButton.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTextField.kt (3 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/OnboardingScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/AddLinkDialog.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/OnboardingProgressBar.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/CareerScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/EmploymentStatusScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/JobInfoScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/JobSeekingScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/LinkScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/MajorScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/MentorshipScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/NameScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/StudentIdScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/model/MajorData.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/model/OnboardingState.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/navigation/OnboardingNavigation.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/onboarding/viewmodel/OnboardingViewModel.kt (1 hunks)
  • app/src/main/res/drawable/ic_onboarding_plus.xml (1 hunks)
  • app/src/main/res/drawable/ic_onboarding_search.xml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (16)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/model/MajorData.kt (1)
app/src/main/java/com/hsLink/hslink/presentation/community/screen/main/CommunityScreen.kt (1)
  • id (45-50)
app/src/main/res/drawable/ic_onboarding_search.xml (1)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTopBar.kt (1)
  • Icon (60-66)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/OnboardingScreen.kt (11)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/StudentIdScreen.kt (1)
  • StudentIdScreen (27-80)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/NameScreen.kt (1)
  • NameScreen (24-77)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/MajorScreen.kt (1)
  • MajorScreen (39-142)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/EmploymentStatusScreen.kt (1)
  • EmploymentStatusScreen (26-76)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/CareerScreen.kt (1)
  • CareerScreen (30-87)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/JobInfoScreen.kt (1)
  • JobInfoScreen (29-228)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/JobSeekingScreen.kt (1)
  • JobSeekingScreen (19-66)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/MentorshipScreen.kt (1)
  • MentorshipScreen (19-66)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/LinkScreen.kt (1)
  • LinksScreen (41-136)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/OnboardingProgressBar.kt (1)
  • OnboardingProgressBar (14-34)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkActionButton.kt (1)
  • HsLinkActionButton (37-93)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/AddLinkDialog.kt (3)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTextField.kt (1)
  • HsLinkTextField (35-107)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkSelectButton.kt (1)
  • HsLinkSelectButton (38-94)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/MajorScreen.kt (1)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTextField.kt (1)
  • HsLinkTextField (35-107)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/EmploymentStatusScreen.kt (2)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkSelectButton.kt (1)
  • HsLinkSelectButton (38-94)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/JobSeekingScreen.kt (2)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkSelectButton.kt (1)
  • HsLinkSelectButton (38-94)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkActionButton.kt (1)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/navigation/OnboardingNavigation.kt (2)
app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt (1)
  • navigate (37-55)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/OnboardingScreen.kt (1)
  • OnboardingRoute (37-162)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/StudentIdScreen.kt (2)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTextField.kt (1)
  • HsLinkTextField (35-107)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/JobInfoScreen.kt (3)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTextField.kt (1)
  • HsLinkTextField (35-107)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkSelectButton.kt (1)
  • HsLinkSelectButton (38-94)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt (1)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/navigation/OnboardingNavigation.kt (1)
  • onboardingNavGraph (18-30)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/LinkScreen.kt (2)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkSelectButton.kt (1)
  • HsLinkSelectButton (38-94)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/AddLinkDialog.kt (1)
  • AddLinkDialog (32-153)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/CareerScreen.kt (1)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/MentorshipScreen.kt (2)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkSelectButton.kt (1)
  • HsLinkSelectButton (38-94)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/NameScreen.kt (1)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTextField.kt (1)
  • HsLinkTextField (35-107)
🪛 detekt (1.23.8)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/LinkScreen.kt

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

(detekt.exceptions.SwallowedException)

🔇 Additional comments (11)
app/src/main/res/drawable/ic_onboarding_search.xml (1)

1-9: Vector drawable looks good.

Proper XML structure, correct dimensions (24×24dp with matching viewport), valid path syntax, and appropriate color choice for a search icon.

app/src/main/res/drawable/ic_onboarding_plus.xml (1)

1-9: Vector drawable looks good.

Proper XML structure, correct dimensions (14×14dp with matching viewport), valid path syntax, and appropriate color choice for an add/action icon.

app/src/main/java/com/hsLink/hslink/presentation/onboarding/model/MajorData.kt (1)

3-21: LGTM!

The static major list is cleanly implemented. Consider externalizing this data to a configuration file or database if the list needs frequent updates or localization support in the future.

app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt (2)

25-25: LGTM! Start destination updated for onboarding flow.

The change from Home to Onboarding aligns with the PR objective. As noted in the PR description, developers can modify the startDestination locally during development if needed.


73-75: LGTM! Navigation method added for onboarding completion.

The navigateToHome method correctly delegates to the navigation controller and follows the same pattern as other navigation methods in the class.

app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt (1)

47-51: LGTM! Onboarding navigation graph integrated correctly.

The onboarding navigation graph is properly wired with appropriate callbacks, following the same pattern as other navigation graphs in the host.

app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTextField.kt (2)

44-44: LGTM! Keyboard options parameter enhances flexibility.

The addition of keyboardOptions parameter with a sensible default and proper merging with imeAction enables callers to configure keyboard behavior while maintaining backward compatibility.

Also applies to: 70-70


93-93: Verify this intentional style change aligns with design specs and maintains readability.

The change from body_16Normal to caption_14Normal for placeholder text is confirmed to be deliberate (commit feat/#16), affecting all 11 HsLinkTextField usages across onboarding, community writing, and dialog screens. Confirm:

  • Change is approved by design and intentional
  • Placeholder text remains readable at the smaller caption size (especially on mobile)
  • Visual hierarchy is maintained across all affected screens (StudentIdScreen, NameScreen, MajorScreen, JobInfoScreen, CommunityWritingScreen, etc.)
app/src/main/java/com/hsLink/hslink/presentation/onboarding/navigation/OnboardingNavigation.kt (1)

12-33: LGTM!

The navigation setup follows Compose Navigation best practices with type-safe routing using Kotlin serialization. The integration with OnboardingRoute is clean and the callbacks are properly wired.

app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/StudentIdScreen.kt (1)

28-80: LGTM!

The implementation correctly handles student ID input with appropriate keyboard configuration (numeric type), proper validation, and clean focus state management. The onDone action properly validates the input before triggering navigation.

app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/NameScreen.kt (1)

25-77: LGTM!

The implementation follows the established onboarding screen pattern with proper validation, focus management, and clear user guidance about using real names. The IME action correctly validates before proceeding.

@@ -0,0 +1,164 @@
package com.hsLink.hslink.presentation.onboarding.screen
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix package name mismatch.

The package declaration is com.hsLink.hslink.presentation.onboarding.screen but the file is located in the component folder. This will cause compilation errors.

Apply this diff:

-package com.hsLink.hslink.presentation.onboarding.screen
+package com.hsLink.hslink.presentation.onboarding.component
📝 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
package com.hsLink.hslink.presentation.onboarding.screen
package com.hsLink.hslink.presentation.onboarding.component
🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/AddLinkDialog.kt
around line 1, the package declaration is incorrect for the file's folder;
change the package declaration to "package
com.hsLink.hslink.presentation.onboarding.component" (or alternatively move the
file into the "screen" folder to match the current package), then save and
recompile to ensure the package path and filesystem location match.

Comment on lines +78 to +83
CareerSelectButton(
modifier = Modifier.padding(top = 16.dp),
onClick = {},
label = "커리어 추가하기",
isEnabled = true
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Wire up the “커리어 추가하기” CTA

The primary button renders but does nothing because onClick is empty. Users will think the screen is broken. Please route it to onNextClick (or another handler that opens the career form).

-                CareerSelectButton(
-                    modifier = Modifier.padding(top = 16.dp),
-                    onClick = {},
+                CareerSelectButton(
+                    modifier = Modifier.padding(top = 16.dp),
+                    onClick = onNextClick,
                     label = "커리어 추가하기",
                     isEnabled = true
                 )
📝 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
CareerSelectButton(
modifier = Modifier.padding(top = 16.dp),
onClick = {},
label = "커리어 추가하기",
isEnabled = true
)
CareerSelectButton(
modifier = Modifier.padding(top = 16.dp),
onClick = onNextClick,
label = "커리어 추가하기",
isEnabled = true
)
🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/CareerScreen.kt
around lines 78 to 83, the CareerSelectButton's onClick is currently empty so
the "커리어 추가하기" CTA does nothing; replace the empty lambda with the appropriate
handler (e.g. onNextClick or the method that opens the career form) so clicking
the button triggers navigation/opening of the career form and keep the existing
modifier, label, and isEnabled settings.

}
},
progress = progress,
subtitle = "자세한 알아주시면 더 맞은 도움을 드릴 수 있어요",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix grammatical error in Korean text.

The subtitle text has grammatical issues. The correct form should be "자세히 알려주시면 더 나은 도움을 드릴 수 있어요" (using "알려주시면" instead of "알아주시면" and "나은" instead of "맞은").

Apply this diff:

-        subtitle = "자세한 알아주시면 더 맞은 도움을 드릴 수 있어요",
+        subtitle = "자세히 알려주시면 더 나은 도움을 드릴 수 있어요",
📝 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
subtitle = "자세한 알아주시면맞은 도움을 드릴 수 있어요",
subtitle = "자세히 알려주시면나은 도움을 드릴 수 있어요",
🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/EmploymentStatusScreen.kt
around line 44, the subtitle string is grammatically incorrect; replace "자세한
알아주시면 더 맞은 도움을 드릴 수 있어요" with the corrected text "자세히 알려주시면 더 나은 도움을 드릴 수 있어요"
(update the string literal only, preserving surrounding code and punctuation).

Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
text = buildAnnotatedString {
append("현재 재직 여부 (형식 : 24.04) ")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify the field label.

The label "현재 재직 여부 (형식 : 24.04)" translates to "Current employment status (format: 24.04)" but the fields below ask for start and end dates. Consider changing to "재직 기간 (형식 : 24.04)" (Employment period) for better clarity.

Apply this diff:

-                        append("현재 재직 여부 (형식 : 24.04) ")
+                        append("재직 기간 (형식 : 24.04) ")
📝 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
append("현재 재직 여부 (형식 : 24.04) ")
append("재직 기간 (형식 : 24.04) ")
🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/JobInfoScreen.kt
around line 81, the label currently reads "현재 재직 여부 (형식 : 24.04)" which is
misleading because the inputs collect start and end dates; change the label to
"재직 기간 (형식 : 24.04)" to accurately indicate employment period, replacing the
existing string literal where append(...) is called so the UI displays the
clearer wording.

Comment on lines +178 to +196
private fun String.extractDomain(): String {
return try {
val withoutProtocol = this.removePrefix("https://")
.removePrefix("http://")
.removePrefix("www.")

val domain = withoutProtocol.split("/")[0]

when {
domain.contains("instagram.com") -> "인스타그램"
domain.contains("github.com") -> "깃허브"
domain.contains("linkedin.com") -> "링크드인"
domain.contains("notion.so") || domain.contains("notion.site") -> "노션"
else -> domain.take(30)
}
} catch (e: Exception) {
this.take(30)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add logging for swallowed exception.

The exception in the extractDomain function is silently swallowed, which can hide parsing errors and make debugging difficult. Consider adding logging to track when domain extraction fails.

Apply this diff:

+import android.util.Log
+
 private fun String.extractDomain(): String {
     return try {
         val withoutProtocol = this.removePrefix("https://")
             .removePrefix("http://")
             .removePrefix("www.")
 
         val domain = withoutProtocol.split("/")[0]
 
         when {
             domain.contains("instagram.com") -> "인스타그램"
             domain.contains("github.com") -> "깃허브"
             domain.contains("linkedin.com") -> "링크드인"
             domain.contains("notion.so") || domain.contains("notion.site") -> "노션"
             else -> domain.take(30)
         }
     } catch (e: Exception) {
+        Log.w("LinkScreen", "Failed to extract domain from URL: $this", e)
         this.take(30)
     }
 }
📝 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
private fun String.extractDomain(): String {
return try {
val withoutProtocol = this.removePrefix("https://")
.removePrefix("http://")
.removePrefix("www.")
val domain = withoutProtocol.split("/")[0]
when {
domain.contains("instagram.com") -> "인스타그램"
domain.contains("github.com") -> "깃허브"
domain.contains("linkedin.com") -> "링크드인"
domain.contains("notion.so") || domain.contains("notion.site") -> "노션"
else -> domain.take(30)
}
} catch (e: Exception) {
this.take(30)
}
}
import android.util.Log
private fun String.extractDomain(): String {
return try {
val withoutProtocol = this.removePrefix("https://")
.removePrefix("http://")
.removePrefix("www.")
val domain = withoutProtocol.split("/")[0]
when {
domain.contains("instagram.com") -> "인스타그램"
domain.contains("github.com") -> "깃허브"
domain.contains("linkedin.com") -> "링크드인"
domain.contains("notion.so") || domain.contains("notion.site") -> "노션"
else -> domain.take(30)
}
} catch (e: Exception) {
Log.w("LinkScreen", "Failed to extract domain from URL: $this", e)
this.take(30)
}
}
🧰 Tools
🪛 detekt (1.23.8)

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

(detekt.exceptions.SwallowedException)

🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/LinkScreen.kt
around lines 178 to 196, the extractDomain() function swallows exceptions
silently; update the catch block to log the caught exception (including message
and stack trace) using the project's logging approach (e.g., Timber.e or Android
Log.e or your app logger) before returning this.take(30), ensuring no behavior
change other than adding the log so parsing failures are visible for debugging.

HsLinkTheme {
LinksScreen(
links = listOf(
ExternalLink(LinkType.GITHUB, "https://instagram.com/username"),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix preview data inconsistency.

The preview data uses LinkType.GITHUB but pairs it with an Instagram URL. This is confusing and should be corrected for clarity.

Apply this diff:

             links = listOf(
-                ExternalLink(LinkType.GITHUB, "https://instagram.com/username"),
+                ExternalLink(LinkType.INSTAGRAM, "https://instagram.com/username"),
                 ExternalLink(LinkType.PORTFOLIO, "https://drive.google.com/...")
             ),
📝 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
ExternalLink(LinkType.GITHUB, "https://instagram.com/username"),
ExternalLink(LinkType.INSTAGRAM, "https://instagram.com/username"),
🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/LinkScreen.kt
around line 204, the preview entry uses LinkType.GITHUB with an Instagram URL
which is inconsistent; update the LinkType to LinkType.INSTAGRAM (or
alternatively change the URL to a GitHub URL) so the LinkType matches the
provided URL in the preview data.

append("*")
}
},
subtitle = "모든 사용자는 멘티로 사작하며, 원할경우 멘토로드 활동에서 초대되니 구독 제공을 받을 수 있어요.",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix typos in user-facing subtitle text.

The subtitle contains several typos that affect readability and meaning:

  • "사작하며" → "시작하며" (start)
  • "원할경우" → "원할 경우" (spacing)
  • "멘토로드" → "멘토로도" (also as mentor)
  • "활동에서 초대되니 구독 제공을" → awkward phrasing

Apply this diff:

-subtitle = "모든 사용자는 멘티로 사작하며, 원할경우 멘토로드 활동에서 초대되니 구독 제공을 받을 수 있어요.",
+subtitle = "모든 사용자는 멘티로 시작하며, 원할 경우 멘토로도 활동하여 조언을 제공할 수 있어요.",
🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/onboarding/component/screen/MentorshipScreen.kt
around line 35, the subtitle string contains multiple typos and awkward
phrasing; replace it with a corrected, natural Korean sentence such as: "모든 사용자는
멘티로 시작하며, 원할 경우 멘토로도 활동에 초대되어 구독 혜택을 받을 수 있어요." to fix spelling, spacing,
grammar, and clarity.

Comment on lines +39 to +45
enum class EmploymentStatus(val label: String) {
WORKING("재학 중"),
GRADUATED("졸업"),
EXPECTED_GRADUATION("졸업예정"),
COMPLETED("수료"),
LEAVE("휴학")
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix semantic mismatch in EmploymentStatus.WORKING.

The enum constant WORKING has the label "재학 중" (currently enrolled/attending school), which doesn't match its name. This semantic mismatch could lead to confusion and incorrect data handling throughout the onboarding flow.

Consider renaming the enum constant to ENROLLED or changing the label to match "WORKING".

Apply this diff to rename to ENROLLED:

 enum class EmploymentStatus(val label: String) {
-    WORKING("재학 중"),
+    ENROLLED("재학 중"),
     GRADUATED("졸업"),
     EXPECTED_GRADUATION("졸업예정"),
     COMPLETED("수료"),
     LEAVE("휴학")
 }
📝 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 EmploymentStatus(val label: String) {
WORKING("재학 중"),
GRADUATED("졸업"),
EXPECTED_GRADUATION("졸업예정"),
COMPLETED("수료"),
LEAVE("휴학")
}
enum class EmploymentStatus(val label: String) {
ENROLLED("재학 중"),
GRADUATED("졸업"),
EXPECTED_GRADUATION("졸업예정"),
COMPLETED("수료"),
LEAVE("휴학")
}
🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/onboarding/model/OnboardingState.kt
around lines 39 to 45, the enum constant WORKING has a label "재학 중" which
semantically mismatches; rename the constant to ENROLLED (preserving the label
"재학 중") and update all references/usages across the codebase (tests,
serializers, mappings, switch/case or when blocks, database fields if
applicable) to use EmploymentStatus.ENROLLED to avoid breakage; run a global
search-and-replace for EmploymentStatus.WORKING -> EmploymentStatus.ENROLLED and
adjust any string comparisons or persisted values accordingly.

Comment on lines +90 to +121
fun moveToNextStep() {
_state.update {
val nextStep = when (it.currentStep) {
OnboardingStep.STUDENT_ID -> OnboardingStep.NAME
OnboardingStep.NAME -> OnboardingStep.MAJOR
OnboardingStep.MAJOR -> OnboardingStep.EMPLOYMENT_STATUS
OnboardingStep.EMPLOYMENT_STATUS -> OnboardingStep.CAREER
OnboardingStep.CAREER -> OnboardingStep.JOB_INFO
OnboardingStep.JOB_INFO -> OnboardingStep.MENTORSHIP
OnboardingStep.MENTORSHIP -> OnboardingStep.LINKS
OnboardingStep.LINKS -> OnboardingStep.JOB_SEEKING
OnboardingStep.JOB_SEEKING -> OnboardingStep.MENTORSHIP
}
it.copy(currentStep = nextStep)
}
}

fun moveToPreviousStep() {
_state.update {
val previousStep = when (it.currentStep) {
OnboardingStep.STUDENT_ID -> OnboardingStep.STUDENT_ID
OnboardingStep.NAME -> OnboardingStep.STUDENT_ID
OnboardingStep.MAJOR -> OnboardingStep.NAME
OnboardingStep.EMPLOYMENT_STATUS -> OnboardingStep.MAJOR
OnboardingStep.CAREER -> OnboardingStep.EMPLOYMENT_STATUS
OnboardingStep.JOB_INFO -> OnboardingStep.CAREER
OnboardingStep.MENTORSHIP -> OnboardingStep.JOB_INFO
OnboardingStep.LINKS -> OnboardingStep.MENTORSHIP
OnboardingStep.JOB_SEEKING -> OnboardingStep.LINKS
}
it.copy(currentStep = previousStep)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix step progression to include JobSeeking and avoid loops

moveToNextStep jumps from JOB_INFO straight to MENTORSHIP and later loops from LINKS → JOB_SEEKING → MENTORSHIP, so the JobSeeking screen is never reached in order and the flow oscillates. Mirror the UI order (…JOB_INFO → JOB_SEEKING → MENTORSHIP → LINKS) and align the reverse mapping.

             val nextStep = when (it.currentStep) {
                 OnboardingStep.STUDENT_ID -> OnboardingStep.NAME
                 OnboardingStep.NAME -> OnboardingStep.MAJOR
                 OnboardingStep.MAJOR -> OnboardingStep.EMPLOYMENT_STATUS
                 OnboardingStep.EMPLOYMENT_STATUS -> OnboardingStep.CAREER
                 OnboardingStep.CAREER -> OnboardingStep.JOB_INFO
-                OnboardingStep.JOB_INFO -> OnboardingStep.MENTORSHIP
-                OnboardingStep.MENTORSHIP -> OnboardingStep.LINKS
-                OnboardingStep.LINKS -> OnboardingStep.JOB_SEEKING
-                OnboardingStep.JOB_SEEKING -> OnboardingStep.MENTORSHIP
+                OnboardingStep.JOB_INFO -> OnboardingStep.JOB_SEEKING
+                OnboardingStep.JOB_SEEKING -> OnboardingStep.MENTORSHIP
+                OnboardingStep.MENTORSHIP -> OnboardingStep.LINKS
+                OnboardingStep.LINKS -> OnboardingStep.LINKS
             }
             val previousStep = when (it.currentStep) {
                 OnboardingStep.STUDENT_ID -> OnboardingStep.STUDENT_ID
                 OnboardingStep.NAME -> OnboardingStep.STUDENT_ID
                 OnboardingStep.MAJOR -> OnboardingStep.NAME
                 OnboardingStep.EMPLOYMENT_STATUS -> OnboardingStep.MAJOR
                 OnboardingStep.CAREER -> OnboardingStep.EMPLOYMENT_STATUS
-                OnboardingStep.JOB_INFO -> OnboardingStep.CAREER
-                OnboardingStep.MENTORSHIP -> OnboardingStep.JOB_INFO
-                OnboardingStep.LINKS -> OnboardingStep.MENTORSHIP
-                OnboardingStep.JOB_SEEKING -> OnboardingStep.LINKS
+                OnboardingStep.JOB_INFO -> OnboardingStep.CAREER
+                OnboardingStep.JOB_SEEKING -> OnboardingStep.JOB_INFO
+                OnboardingStep.MENTORSHIP -> OnboardingStep.JOB_SEEKING
+                OnboardingStep.LINKS -> OnboardingStep.MENTORSHIP
             }
🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/onboarding/viewmodel/OnboardingViewModel.kt
around lines 90–121, the next/previous step mappings are incorrect causing
JOB_SEEKING to be skipped and a loop LINKS→JOB_SEEKING→MENTORSHIP; update
moveToNextStep to follow the UI order JOB_INFO → JOB_SEEKING → MENTORSHIP →
LINKS (so JOB_INFO maps to JOB_SEEKING, JOB_SEEKING maps to MENTORSHIP,
MENTORSHIP maps to LINKS, and LINKS should remain LINKS or advance to a terminal
state as appropriate), and update moveToPreviousStep to mirror that order in
reverse (so JOB_SEEKING maps back to JOB_INFO, MENTORSHIP back to JOB_SEEKING,
LINKS back to MENTORSHIP), keeping STUDENT_ID as the lower bound; make these
when-branch changes to ensure linear, non-looping progression.

@vvan2 vvan2 merged commit 0b829b5 into develop Nov 5, 2025
3 checks passed
@vvan2 vvan2 deleted the feat/#16-onboarding-ui branch November 5, 2025 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature ✨ 기능 구현 주완🐹 주완 전용 라벨

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 온보딩 UI 작업

2 participants