diff --git a/app/src/main/java/com/threegap/bitnagil/MainNavHost.kt b/app/src/main/java/com/threegap/bitnagil/MainNavHost.kt index 39fd583d..0df6644b 100644 --- a/app/src/main/java/com/threegap/bitnagil/MainNavHost.kt +++ b/app/src/main/java/com/threegap/bitnagil/MainNavHost.kt @@ -14,6 +14,9 @@ import com.threegap.bitnagil.presentation.onboarding.OnBoardingScreenContainer import com.threegap.bitnagil.presentation.onboarding.OnBoardingViewModel import com.threegap.bitnagil.presentation.onboarding.model.navarg.OnBoardingScreenArg import com.threegap.bitnagil.presentation.report.ReportScreenContainer +import com.threegap.bitnagil.presentation.reportdetail.ReportDetailScreenContainer +import com.threegap.bitnagil.presentation.reportdetail.ReportDetailViewModel +import com.threegap.bitnagil.presentation.reportdetail.model.navarg.ReportDetailScreenArg import com.threegap.bitnagil.presentation.reporthistory.ReportHistoryScreenContainer import com.threegap.bitnagil.presentation.routinelist.RoutineListScreenContainer import com.threegap.bitnagil.presentation.setting.SettingScreenContainer @@ -319,7 +322,27 @@ fun MainNavHost( navigator.navController.popBackStack() } }, - navigateToReportDetail = { + navigateToReportDetail = { reportId -> + navigator.navController.navigate(Route.ReportDetail(reportId = reportId)) { + launchSingleTop = true + } + }, + ) + } + + composable { navBackStackEntry -> + val arg = navBackStackEntry.toRoute() + val reportDetailScreenArg = ReportDetailScreenArg(reportId = arg.reportId) + val viewModel = hiltViewModel { factory -> + factory.create(reportDetailScreenArg) + } + + ReportDetailScreenContainer( + viewModel = viewModel, + navigateToBack = { + if (navigator.navController.previousBackStackEntry != null) { + navigator.navController.popBackStack() + } }, ) } diff --git a/app/src/main/java/com/threegap/bitnagil/Route.kt b/app/src/main/java/com/threegap/bitnagil/Route.kt index 5a9ad70e..7c056d30 100644 --- a/app/src/main/java/com/threegap/bitnagil/Route.kt +++ b/app/src/main/java/com/threegap/bitnagil/Route.kt @@ -54,4 +54,7 @@ sealed interface Route { @Serializable data object ReportHistory : Route + + @Serializable + data class ReportDetail(val reportId: String) : Route } diff --git a/data/src/main/java/com/threegap/bitnagil/data/address/model/response/Coord2AddressResponse.kt b/data/src/main/java/com/threegap/bitnagil/data/address/model/response/Coord2AddressResponse.kt index 6feac294..9769ce59 100644 --- a/data/src/main/java/com/threegap/bitnagil/data/address/model/response/Coord2AddressResponse.kt +++ b/data/src/main/java/com/threegap/bitnagil/data/address/model/response/Coord2AddressResponse.kt @@ -20,7 +20,7 @@ data class Meta( @Serializable data class Document( @SerialName("road_address") - val roadAddress: RoadAddress, + val roadAddress: RoadAddress?, @SerialName("address") val address: Address, ) diff --git a/data/src/main/java/com/threegap/bitnagil/data/report/datasource/ReportDataSource.kt b/data/src/main/java/com/threegap/bitnagil/data/report/datasource/ReportDataSource.kt index e672d191..82cdf53a 100644 --- a/data/src/main/java/com/threegap/bitnagil/data/report/datasource/ReportDataSource.kt +++ b/data/src/main/java/com/threegap/bitnagil/data/report/datasource/ReportDataSource.kt @@ -1,9 +1,11 @@ package com.threegap.bitnagil.data.report.datasource import com.threegap.bitnagil.data.report.model.request.ReportRequestDto +import com.threegap.bitnagil.data.report.model.response.ReportDetailDto import com.threegap.bitnagil.data.report.model.response.ReportHistoriesPerDateDto interface ReportDataSource { suspend fun submitReport(reportRequestDto: ReportRequestDto): Result suspend fun getReports(): Result + suspend fun getReport(reportId: String): Result } diff --git a/data/src/main/java/com/threegap/bitnagil/data/report/datasourceImpl/ReportDataSourceImpl.kt b/data/src/main/java/com/threegap/bitnagil/data/report/datasourceImpl/ReportDataSourceImpl.kt index c980beef..0351d514 100644 --- a/data/src/main/java/com/threegap/bitnagil/data/report/datasourceImpl/ReportDataSourceImpl.kt +++ b/data/src/main/java/com/threegap/bitnagil/data/report/datasourceImpl/ReportDataSourceImpl.kt @@ -3,6 +3,7 @@ package com.threegap.bitnagil.data.report.datasourceImpl import com.threegap.bitnagil.data.common.safeApiCall import com.threegap.bitnagil.data.report.datasource.ReportDataSource import com.threegap.bitnagil.data.report.model.request.ReportRequestDto +import com.threegap.bitnagil.data.report.model.response.ReportDetailDto import com.threegap.bitnagil.data.report.model.response.ReportHistoriesPerDateDto import com.threegap.bitnagil.data.report.service.ReportService import javax.inject.Inject @@ -17,4 +18,8 @@ class ReportDataSourceImpl @Inject constructor( override suspend fun getReports(): Result { return safeApiCall { reportService.getReports() } } + + override suspend fun getReport(reportId: String): Result { + return safeApiCall { reportService.getReport(reportId = reportId) } + } } diff --git a/data/src/main/java/com/threegap/bitnagil/data/report/model/response/ReportDetailDto.kt b/data/src/main/java/com/threegap/bitnagil/data/report/model/response/ReportDetailDto.kt new file mode 100644 index 00000000..3c9cb097 --- /dev/null +++ b/data/src/main/java/com/threegap/bitnagil/data/report/model/response/ReportDetailDto.kt @@ -0,0 +1,38 @@ +package com.threegap.bitnagil.data.report.model.response + +import com.threegap.bitnagil.domain.report.model.ReportCategory +import com.threegap.bitnagil.domain.report.model.ReportDetail +import com.threegap.bitnagil.domain.report.model.ReportStatus +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.time.LocalDate + +@Serializable +class ReportDetailDto( + @SerialName("reportDate") + val reportDate: String, + @SerialName("reportStatus") + val reportStatus: String, + @SerialName("reportTitle") + val reportTitle: String, + @SerialName("reportContent") + val reportContent: String, + @SerialName("reportCategory") + val reportCategory: String, + @SerialName("reportLocation") + val reportLocation: String, + @SerialName("reportImageUrls") + val reportImageUrls: List, +) + +fun ReportDetailDto.toDomain(id: String?): ReportDetail = + ReportDetail( + id = id ?: "", + date = LocalDate.parse(this.reportDate), + status = ReportStatus.fromString(this.reportStatus), + title = this.reportTitle, + content = this.reportContent, + category = ReportCategory.fromString(this.reportCategory), + address = this.reportLocation, + imageUrls = this.reportImageUrls, + ) diff --git a/data/src/main/java/com/threegap/bitnagil/data/report/repositoryImpl/ReportRepositoryImpl.kt b/data/src/main/java/com/threegap/bitnagil/data/report/repositoryImpl/ReportRepositoryImpl.kt index dcfe9e36..07cc0f32 100644 --- a/data/src/main/java/com/threegap/bitnagil/data/report/repositoryImpl/ReportRepositoryImpl.kt +++ b/data/src/main/java/com/threegap/bitnagil/data/report/repositoryImpl/ReportRepositoryImpl.kt @@ -2,8 +2,10 @@ package com.threegap.bitnagil.data.report.repositoryImpl import com.threegap.bitnagil.data.report.datasource.ReportDataSource import com.threegap.bitnagil.data.report.model.request.toDto +import com.threegap.bitnagil.data.report.model.response.toDomain import com.threegap.bitnagil.data.report.model.response.toDomainMap import com.threegap.bitnagil.domain.report.model.Report +import com.threegap.bitnagil.domain.report.model.ReportDetail import com.threegap.bitnagil.domain.report.model.ReportItem import com.threegap.bitnagil.domain.report.repository.ReportRepository import java.time.LocalDate @@ -19,4 +21,8 @@ class ReportRepositoryImpl @Inject constructor( override suspend fun getReportHistories(): Result>> { return reportDataSource.getReports().map { it.toDomainMap() } } + + override suspend fun getReport(reportId: String): Result { + return reportDataSource.getReport(reportId = reportId).map { it.toDomain(id = reportId) } + } } diff --git a/data/src/main/java/com/threegap/bitnagil/data/report/service/ReportService.kt b/data/src/main/java/com/threegap/bitnagil/data/report/service/ReportService.kt index f6d25128..f89d71de 100644 --- a/data/src/main/java/com/threegap/bitnagil/data/report/service/ReportService.kt +++ b/data/src/main/java/com/threegap/bitnagil/data/report/service/ReportService.kt @@ -1,11 +1,13 @@ package com.threegap.bitnagil.data.report.service import com.threegap.bitnagil.data.report.model.request.ReportRequestDto +import com.threegap.bitnagil.data.report.model.response.ReportDetailDto import com.threegap.bitnagil.data.report.model.response.ReportHistoriesPerDateDto import com.threegap.bitnagil.network.model.BaseResponse import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST +import retrofit2.http.Path interface ReportService { @POST("/api/v2/reports") @@ -15,4 +17,9 @@ interface ReportService { @GET("/api/v2/reports") suspend fun getReports(): BaseResponse + + @GET("/api/v2/reports/{reportId}") + suspend fun getReport( + @Path("reportId") reportId: String, + ): BaseResponse } diff --git a/domain/src/main/java/com/threegap/bitnagil/domain/report/model/ReportDetail.kt b/domain/src/main/java/com/threegap/bitnagil/domain/report/model/ReportDetail.kt new file mode 100644 index 00000000..cc764d1d --- /dev/null +++ b/domain/src/main/java/com/threegap/bitnagil/domain/report/model/ReportDetail.kt @@ -0,0 +1,14 @@ +package com.threegap.bitnagil.domain.report.model + +import java.time.LocalDate + +data class ReportDetail( + val id: String, + val title: String, + val content: String, + val category: ReportCategory, + val status: ReportStatus, + val imageUrls: List, + val address: String, + val date: LocalDate, +) diff --git a/domain/src/main/java/com/threegap/bitnagil/domain/report/repository/ReportRepository.kt b/domain/src/main/java/com/threegap/bitnagil/domain/report/repository/ReportRepository.kt index 39a68033..512b5b8b 100644 --- a/domain/src/main/java/com/threegap/bitnagil/domain/report/repository/ReportRepository.kt +++ b/domain/src/main/java/com/threegap/bitnagil/domain/report/repository/ReportRepository.kt @@ -1,10 +1,12 @@ package com.threegap.bitnagil.domain.report.repository import com.threegap.bitnagil.domain.report.model.Report +import com.threegap.bitnagil.domain.report.model.ReportDetail import com.threegap.bitnagil.domain.report.model.ReportItem import java.time.LocalDate interface ReportRepository { suspend fun submitReport(report: Report): Result suspend fun getReportHistories(): Result>> + suspend fun getReport(reportId: String): Result } diff --git a/domain/src/main/java/com/threegap/bitnagil/domain/report/usecase/GetReportUseCase.kt b/domain/src/main/java/com/threegap/bitnagil/domain/report/usecase/GetReportUseCase.kt new file mode 100644 index 00000000..42b8c5da --- /dev/null +++ b/domain/src/main/java/com/threegap/bitnagil/domain/report/usecase/GetReportUseCase.kt @@ -0,0 +1,13 @@ +package com.threegap.bitnagil.domain.report.usecase + +import com.threegap.bitnagil.domain.report.model.ReportDetail +import com.threegap.bitnagil.domain.report.repository.ReportRepository +import javax.inject.Inject + +class GetReportUseCase @Inject constructor( + private val reportRepository: ReportRepository, +) { + suspend operator fun invoke(id: String): Result { + return reportRepository.getReport(reportId = id) + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/ReportDetailScreen.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/ReportDetailScreen.kt new file mode 100644 index 00000000..6a5d0dbb --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/ReportDetailScreen.kt @@ -0,0 +1,150 @@ +package com.threegap.bitnagil.presentation.reportdetail + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage +import coil3.request.ImageRequest +import com.threegap.bitnagil.designsystem.BitnagilTheme +import com.threegap.bitnagil.designsystem.component.block.BitnagilTopBar +import com.threegap.bitnagil.presentation.reportdetail.component.atom.ReportProcessBadge +import com.threegap.bitnagil.presentation.reportdetail.component.block.ReportDetailLabeledContent +import com.threegap.bitnagil.presentation.reportdetail.model.mvi.ReportDetailState +import com.threegap.bitnagil.presentation.reportdetail.util.toPresentationFormatInReportDetail +import org.orbitmvi.orbit.compose.collectAsState + +@Composable +fun ReportDetailScreenContainer( + viewModel: ReportDetailViewModel, + navigateToBack: () -> Unit, +) { + val state by viewModel.collectAsState() + + ReportDetailScreen( + state = state, + onClickPreviousButton = navigateToBack, + ) +} + +@Composable +private fun ReportDetailScreen( + modifier: Modifier = Modifier, + onClickPreviousButton: () -> Unit, + state: ReportDetailState, +) { + val verticalScrollState = rememberScrollState() + + Column( + modifier = modifier + .fillMaxSize() + .background(color = BitnagilTheme.colors.white) + .statusBarsPadding(), + ) { + BitnagilTopBar( + title = "제보하기", + showBackButton = true, + onBackClick = onClickPreviousButton, + ) + + Column( + modifier = Modifier + .weight(1f) + .verticalScroll(verticalScrollState) + .padding(horizontal = 20.dp), + ) { + Spacer(modifier = Modifier.height(20.dp)) + + ReportProcessBadge(reportProcess = state.reportProcess) + + Spacer(modifier = Modifier.height(6.dp)) + + Text( + text = state.date.toPresentationFormatInReportDetail(), + style = BitnagilTheme.typography.subtitle1SemiBold, + ) + + Spacer(modifier = Modifier.height(14.dp)) + + Row { + state.imageUrls.forEach { imageUrl -> + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(imageUrl) + .build(), + modifier = Modifier + .size(74.dp) + .clip(shape = RoundedCornerShape(9.dp)) + .background(color = BitnagilTheme.colors.black), + contentScale = ContentScale.Crop, + contentDescription = null, + ) + } + } + + Spacer(modifier = Modifier.height(24.dp)) + + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(28.dp), + ) { + ReportDetailLabeledContent( + label = "제목", + content = state.reportTitle, + ) + + ReportDetailLabeledContent( + label = "카테고리", + content = state.reportCategory.title, + ) + + ReportDetailLabeledContent( + label = "상세 제목 내용", + content = state.reportContent, + ) + + ReportDetailLabeledContent( + label = "내 위치", + content = state.location, + ) + } + + Spacer(modifier = Modifier.height(36.dp)) + } + } +} + +@Composable +@Preview +private fun ReportDetailScreenPreview() { + BitnagilTheme { + ReportDetailScreen( + state = ReportDetailState.Init.copy( + reportContent = "Lorem ipsum dolor sit amet, " + + "consectetur adipiscing elit," + + " sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " + + "Nisl tincidunt eget nullam non.", + ), + onClickPreviousButton = {}, + ) + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/ReportDetailViewModel.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/ReportDetailViewModel.kt new file mode 100644 index 00000000..d28ddc4d --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/ReportDetailViewModel.kt @@ -0,0 +1,53 @@ +package com.threegap.bitnagil.presentation.reportdetail + +import androidx.lifecycle.ViewModel +import com.threegap.bitnagil.domain.report.usecase.GetReportUseCase +import com.threegap.bitnagil.presentation.reportdetail.model.ReportCategory +import com.threegap.bitnagil.presentation.reportdetail.model.ReportProcess +import com.threegap.bitnagil.presentation.reportdetail.model.mvi.ReportDetailSideEffect +import com.threegap.bitnagil.presentation.reportdetail.model.mvi.ReportDetailState +import com.threegap.bitnagil.presentation.reportdetail.model.navarg.ReportDetailScreenArg +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import dagger.hilt.android.lifecycle.HiltViewModel +import org.orbitmvi.orbit.Container +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.viewmodel.container + +@HiltViewModel(assistedFactory = ReportDetailViewModel.Factory::class) +class ReportDetailViewModel @AssistedInject constructor( + private val getReportDetailUseCase: GetReportUseCase, + @Assisted private val reportDetailArg: ReportDetailScreenArg, +) : ContainerHost, ViewModel() { + override val container: Container = container(initialState = ReportDetailState.Init) + + @AssistedFactory + interface Factory { + fun create(reportDetailArg: ReportDetailScreenArg): ReportDetailViewModel + } + + init { + loadReportDetail(reportId = reportDetailArg.reportId) + } + + private fun loadReportDetail(reportId: String) = intent { + getReportDetailUseCase(id = reportId).fold( + onSuccess = { reportDetail -> + reduce { + state.copy( + reportProcess = ReportProcess.fromDomain(reportDetail.status), + reportTitle = reportDetail.title, + reportContent = reportDetail.content, + reportCategory = ReportCategory.fromDomain(reportDetail.category), + imageUrls = reportDetail.imageUrls, + location = reportDetail.address, + date = reportDetail.date, + ) + } + }, + onFailure = { + }, + ) + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/component/atom/ReportProcessBadge.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/component/atom/ReportProcessBadge.kt new file mode 100644 index 00000000..9977e533 --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/component/atom/ReportProcessBadge.kt @@ -0,0 +1,60 @@ +package com.threegap.bitnagil.presentation.reportdetail.component.atom + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.threegap.bitnagil.designsystem.BitnagilTheme +import com.threegap.bitnagil.presentation.reportdetail.model.ReportProcess + +@Composable +fun ReportProcessBadge( + modifier: Modifier = Modifier, + reportProcess: ReportProcess, +) { + Text( + text = reportProcess.title, + style = BitnagilTheme.typography.caption1SemiBold, + color = reportProcess.getProcessBadgeTextColor(), + modifier = modifier + .background(color = reportProcess.getProcessBadgeBackgroundColor(), shape = RoundedCornerShape(6.dp)) + .padding(horizontal = 10.dp, vertical = 4.dp), + ) +} + +@Composable +private fun ReportProcess.getProcessBadgeBackgroundColor(): Color = + when (this) { + ReportProcess.Reported -> BitnagilTheme.colors.green10 + ReportProcess.Progress -> BitnagilTheme.colors.skyBlue10 + else -> BitnagilTheme.colors.coolGray95 + } + +@Composable +private fun ReportProcess.getProcessBadgeTextColor(): Color = + when (this) { + ReportProcess.Reported -> BitnagilTheme.colors.green300 + ReportProcess.Progress -> BitnagilTheme.colors.blue300 + else -> BitnagilTheme.colors.coolGray40 + } + +@Composable +@Preview +private fun ReportProcessBadgePreview() { + BitnagilTheme { + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + ReportProcessBadge(reportProcess = ReportProcess.Progress) + ReportProcessBadge(reportProcess = ReportProcess.Reported) + ReportProcessBadge(reportProcess = ReportProcess.Complete) + } + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/component/block/ReportDetailLabeledContent.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/component/block/ReportDetailLabeledContent.kt new file mode 100644 index 00000000..e623068c --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/component/block/ReportDetailLabeledContent.kt @@ -0,0 +1,42 @@ +package com.threegap.bitnagil.presentation.reportdetail.component.block + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.threegap.bitnagil.designsystem.BitnagilTheme + +@Composable +fun ReportDetailLabeledContent( + modifier: Modifier = Modifier, + label: String, + content: String, +) { + Column( + modifier = modifier, + ) { + Text( + text = label, + style = BitnagilTheme.typography.body2SemiBold, + color = BitnagilTheme.colors.coolGray10, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + modifier = Modifier + .fillMaxWidth() + .background(color = BitnagilTheme.colors.coolGray99, shape = RoundedCornerShape(12.dp)) + .padding(vertical = 16.dp, horizontal = 20.dp), + text = content, + style = BitnagilTheme.typography.body2Medium, + ) + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/ReportCategory.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/ReportCategory.kt new file mode 100644 index 00000000..96a91f1d --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/ReportCategory.kt @@ -0,0 +1,31 @@ +package com.threegap.bitnagil.presentation.reportdetail.model +import com.threegap.bitnagil.domain.report.model.ReportCategory as DomainReportCategory + +enum class ReportCategory( + val title: String, +) { + TrafficFacilities( + title = "교통 시설", + ), + LightingFacilities( + title = "조명 시설", + ), + WaterFacilities( + title = "상하수도 시설", + ), + Amenities( + title = "편의 시설", + ), + ; + + companion object { + fun fromDomain(domainReportCategory: com.threegap.bitnagil.domain.report.model.ReportCategory): ReportCategory { + return when (domainReportCategory) { + DomainReportCategory.TRANSPORTATION -> TrafficFacilities + DomainReportCategory.LIGHTING -> LightingFacilities + DomainReportCategory.WATERFACILITY -> WaterFacilities + DomainReportCategory.AMENITY -> Amenities + } + } + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/ReportProcess.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/ReportProcess.kt new file mode 100644 index 00000000..1fe8cecc --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/ReportProcess.kt @@ -0,0 +1,22 @@ +package com.threegap.bitnagil.presentation.reportdetail.model + +import com.threegap.bitnagil.domain.report.model.ReportStatus + +enum class ReportProcess( + val title: String, +) { + Reported(title = "제보 완료"), + Progress(title = "처리 중"), + Complete(title = "처리 완료"), + ; + + companion object { + fun fromDomain(status: ReportStatus): ReportProcess { + return when (status) { + ReportStatus.Pending -> Reported + ReportStatus.InProgress -> Progress + ReportStatus.Completed -> Complete + } + } + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/mvi/ReportDetailSideEffect.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/mvi/ReportDetailSideEffect.kt new file mode 100644 index 00000000..40bdd9b7 --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/mvi/ReportDetailSideEffect.kt @@ -0,0 +1,3 @@ +package com.threegap.bitnagil.presentation.reportdetail.model.mvi + +interface ReportDetailSideEffect diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/mvi/ReportDetailState.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/mvi/ReportDetailState.kt new file mode 100644 index 00000000..834a5a9f --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/mvi/ReportDetailState.kt @@ -0,0 +1,27 @@ +package com.threegap.bitnagil.presentation.reportdetail.model.mvi + +import com.threegap.bitnagil.presentation.reportdetail.model.ReportCategory +import com.threegap.bitnagil.presentation.reportdetail.model.ReportProcess +import java.time.LocalDate + +data class ReportDetailState( + val reportProcess: ReportProcess, + val reportTitle: String, + val reportContent: String, + val reportCategory: ReportCategory, + val imageUrls: List, + val location: String, + val date: LocalDate, +) { + companion object { + val Init = ReportDetailState( + reportProcess = ReportProcess.Reported, + reportTitle = "", + reportContent = "", + reportCategory = ReportCategory.TrafficFacilities, + imageUrls = emptyList(), + location = "", + date = LocalDate.now(), + ) + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/navarg/ReportDetailScreenArg.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/navarg/ReportDetailScreenArg.kt new file mode 100644 index 00000000..60a68ad1 --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/model/navarg/ReportDetailScreenArg.kt @@ -0,0 +1,8 @@ +package com.threegap.bitnagil.presentation.reportdetail.model.navarg + +import kotlinx.serialization.Serializable + +@Serializable +data class ReportDetailScreenArg( + val reportId: String, +) diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/util/LocalDateUtils.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/util/LocalDateUtils.kt new file mode 100644 index 00000000..67f3c93f --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reportdetail/util/LocalDateUtils.kt @@ -0,0 +1,10 @@ +package com.threegap.bitnagil.presentation.reportdetail.util + +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.util.Locale + +fun LocalDate.toPresentationFormatInReportDetail(): String { + val formatter = DateTimeFormatter.ofPattern("yy.MM.dd (E)", Locale.KOREAN) + return this.format(formatter) +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/reporthistory/component/block/ReportHistoryItem.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/reporthistory/component/block/ReportHistoryItem.kt index 88841c41..57b940ac 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/reporthistory/component/block/ReportHistoryItem.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/reporthistory/component/block/ReportHistoryItem.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -83,6 +84,7 @@ fun ReportHistoryItem( .size(74.dp) .clip(shape = RoundedCornerShape(9.dp)) .background(color = BitnagilTheme.colors.black), + contentScale = ContentScale.Crop, contentDescription = null, ) }