Conversation
Updated the Android CI workflow file name and structure.
kotlinx-serialization 추가 https://github.com/Kotlin/kotlinx.serialization
mapsplatform.secrets-gradle-plugin https://github.com/google/secrets-gradle-plugin
secrets.properties
- Quotation 파일 이동
- 공통 반환값 : ApiResponse - Pageable 반환값 : PagenatedResponseDto - QuotationListItem
- QuotationStatus : 상태 추가 - UserRole : 사용자 상태 정의
- AlarmLocalDataSource, AlarmRemoteDataSource - AlarmRepository - Notification 관련 model
vendor -> supplier
@ApplicationContenxt
- Kotlin Serialization에서는 기본값 or null인 경우 파싱값에서 자동으로 제외하므로 무조건 포함되어야 하는 값이라면 annotation을 추가해야 한다.
- DataStore - ROOM
EverpException
- ErrorActivity : 치명적인 오류 발생시 노출
vendor -> supplier
There was a problem hiding this comment.
Pull Request Overview
This PR establishes the foundational architecture for an Android ERP application, implementing a clean architecture pattern with UI-Domain-Data layers, Hilt dependency injection, comprehensive exception handling, and Firebase integration.
Key Changes:
- Clean architecture setup (UI/Domain/Data layers)
- Hilt dependency injection configuration
- Custom exception handling system with categorized error types
- Network layer with Retrofit and OkHttp
- Firebase services integration (FCM, Analytics, Crashlytics)
Reviewed Changes
Copilot reviewed 173 out of 2032 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| gradle/libs.versions.toml | Added dependencies for Room, DataStore, Firebase, security, and serialization |
| gradle.properties | Enabled Gradle build cache and parallel execution |
| commitlint.config.js | Configured commit message linting rules |
| app/src/main/res/xml/* | Network security and backup configuration |
| domain/exception/* | Comprehensive exception handling framework |
| domain/model/* | Domain models for users, notifications, invoices, orders, suppliers |
| data/repository/* | Repository implementations following clean architecture |
| di/* | Hilt modules for dependency injection |
| ui/* | Screen implementations with Jetpack Compose |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| kotlinxCoroutinesCore = "1.10.2" | ||
| timber = "5.0.1" | ||
| ktlint = "13.1.0" | ||
| secreteGradlePlugin = "2.0.1" |
There was a problem hiding this comment.
Corrected spelling of 'secrete' to 'secret'
| secreteGradlePlugin = "2.0.1" | |
| secretGradlePlugin = "2.0.1" |
| ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } | ||
| hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } | ||
| ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } | ||
| secrets-gradle-plugin = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secreteGradlePlugin" } |
There was a problem hiding this comment.
The version reference 'secreteGradlePlugin' has a spelling error and should be 'secretGradlePlugin'
| override val descriptor: SerialDescriptor = serialDescriptor<String>() | ||
|
|
||
| override fun serialize(encoder: Encoder, value: NotificationSourceEnum) { | ||
| // encoder.encodeString(value.toApiString() ?: ) |
There was a problem hiding this comment.
There is commented-out code on line 14 that references a toApiString() method. If this is the intended implementation, the commented code should be removed. If the toApiString() approach is preferred, it should be uncommented and the current line 15 removed for clarity.
| // encoder.encodeString(value.toApiString() ?: ) |
| Scaffold( | ||
| bottomBar = { CustomNavigationBar(navController, SupplierNavigationItem.allDestinations) }, | ||
| ) { innerPadding -> | ||
| // 2. 고객사용 NavHost |
There was a problem hiding this comment.
Corrected comment - this is the Supplier app, not customer ('고객사' should be '공급업체')
| // 2. 고객사용 NavHost | |
| // 2. 공급업체용 NavHost |
| } | ||
| }, | ||
| // (5) ⭐ 아이콘 동적 변경 | ||
| // TODO 아이콘을 ImageVector로 변경 |
There was a problem hiding this comment.
The TODO comment on line 53 is outdated - the code already uses ImageVector for icons. This TODO should be removed.
| // TODO 아이콘을 ImageVector로 변경 |
| }, | ||
| label = { Text(screen.label) }, | ||
| // (6) ⭐ 색상 동적 변경 | ||
| // TODO NavigationBarItemDefaults.colors()를 사용하여 선택/비선택 상태에 따른 아이콘 및 라벨 색상을 지정 |
There was a problem hiding this comment.
The TODO comment on line 69 is outdated - the code already implements NavigationBarItemDefaults.colors(). This TODO should be removed.
| // TODO NavigationBarItemDefaults.colors()를 사용하여 선택/비선택 상태에 따른 아이콘 및 라벨 색상을 지정 |
| when (status) { | ||
| PermissionStatus.GRANTED -> Timber.tag("TAG").i("Permission Granted") | ||
| PermissionStatus.DENIED -> Timber.tag("TAG").i("Permission Denied") | ||
| PermissionStatus.NEEDS_RATIONALE -> Timber.tag("TAG").i("Permission Needs_rationals") |
There was a problem hiding this comment.
Corrected spelling in log message from 'Needs_rationals' to 'Needs_rationale'
| PermissionStatus.NEEDS_RATIONALE -> Timber.tag("TAG").i("Permission Needs_rationals") | |
| PermissionStatus.NEEDS_RATIONALE -> Timber.tag("TAG").i("Permission Needs_rationale") |
|
|
||
| import androidx.compose.ui.graphics.Color | ||
|
|
||
| enum class SupplierCatetoryEnum { |
There was a problem hiding this comment.
Corrected spelling of enum class name from 'SupplierCatetoryEnum' to 'SupplierCategoryEnum' throughout the file
| enum class SupplierCatetoryEnum { | |
| enum class SupplierCategoryEnum { |
|
|
||
| // 알림 빌더 생성 | ||
| val notification = NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID) | ||
| .setSmallIcon(R.mipmap.ic_launcher) // TODO: 알림용 작은 아이콘으로 교체 권장 |
There was a problem hiding this comment.
Using the launcher icon as the notification small icon is not recommended. The launcher icon is typically too large and complex for a status bar notification icon. Create a dedicated small, monochrome, transparent notification icon following Android's notification icon guidelines.
| .setSmallIcon(R.mipmap.ic_launcher) // TODO: 알림용 작은 아이콘으로 교체 권장 | |
| .setSmallIcon(R.drawable.ic_notification) // 알림용 작은 아이콘 사용 (권장) |
| private fun getAccessToken(): String? { | ||
| // TODO: DataStore 또는 SharedPreferences에서 토큰 가져오기 | ||
| // return tokenManager.getAccessToken() | ||
| return null | ||
| } |
There was a problem hiding this comment.
The AuthInterceptor always returns null for the access token, which means authentication headers are never actually added to requests. This will cause all authenticated API calls to fail with 401 errors. This critical TODO must be implemented before the app can function properly.
요약
주요 변경 사항
관련 이슈
테스트/검증
확인 사항
스크린샷/로그(선택)