Skip to content

Commit 0cfd13e

Browse files
committed
Feat: 제보 상세조회 화면 UI 구현
1 parent 62f0976 commit 0cfd13e

9 files changed

Lines changed: 338 additions & 0 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.threegap.bitnagil.presentation.reportdetail
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.Row
7+
import androidx.compose.foundation.layout.Spacer
8+
import androidx.compose.foundation.layout.fillMaxSize
9+
import androidx.compose.foundation.layout.fillMaxWidth
10+
import androidx.compose.foundation.layout.height
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.foundation.layout.size
13+
import androidx.compose.foundation.layout.statusBarsPadding
14+
import androidx.compose.foundation.rememberScrollState
15+
import androidx.compose.foundation.shape.RoundedCornerShape
16+
import androidx.compose.foundation.verticalScroll
17+
import androidx.compose.material3.Text
18+
import androidx.compose.runtime.Composable
19+
import androidx.compose.ui.Modifier
20+
import androidx.compose.ui.draw.clip
21+
import androidx.compose.ui.platform.LocalContext
22+
import androidx.compose.ui.tooling.preview.Preview
23+
import androidx.compose.ui.unit.dp
24+
import coil3.compose.AsyncImage
25+
import coil3.request.ImageRequest
26+
import com.threegap.bitnagil.designsystem.BitnagilTheme
27+
import com.threegap.bitnagil.designsystem.component.block.BitnagilTopBar
28+
import com.threegap.bitnagil.presentation.reportdetail.component.atom.ReportProcessBadge
29+
import com.threegap.bitnagil.presentation.reportdetail.component.block.ReportDetailLabeledContent
30+
import com.threegap.bitnagil.presentation.reportdetail.model.mvi.ReportDetailState
31+
import com.threegap.bitnagil.presentation.reportdetail.util.toPresentationFormatInReportDetail
32+
33+
@Composable
34+
fun ReportDetailScreenContainer(
35+
36+
) {
37+
38+
}
39+
40+
@Composable
41+
private fun ReportDetailScreen(
42+
modifier: Modifier = Modifier,
43+
onClickPreviousButton: () -> Unit,
44+
state: ReportDetailState,
45+
) {
46+
val verticalScrollState = rememberScrollState()
47+
48+
Column(
49+
modifier = modifier
50+
.fillMaxSize()
51+
.verticalScroll(verticalScrollState)
52+
.background(color = BitnagilTheme.colors.white)
53+
.statusBarsPadding()
54+
.padding(horizontal = 20.dp),
55+
) {
56+
BitnagilTopBar(
57+
title = "제보하기",
58+
showBackButton = true,
59+
onBackClick = onClickPreviousButton,
60+
)
61+
62+
Spacer(modifier = Modifier.height(20.dp))
63+
64+
ReportProcessBadge(reportProcess = state.reportProcess)
65+
66+
Spacer(modifier = Modifier.height(6.dp))
67+
68+
Text(
69+
text = state.date.toPresentationFormatInReportDetail(),
70+
style = BitnagilTheme.typography.subtitle1SemiBold,
71+
)
72+
73+
Spacer(modifier = Modifier.height(14.dp))
74+
75+
Row {
76+
state.imageUrls.forEach { imageUrl ->
77+
AsyncImage(
78+
model = ImageRequest.Builder(LocalContext.current)
79+
.data(imageUrl)
80+
.build(),
81+
modifier = Modifier
82+
.size(74.dp)
83+
.clip(shape = RoundedCornerShape(9.dp))
84+
.background(color = BitnagilTheme.colors.black),
85+
contentDescription = null,
86+
)
87+
}
88+
}
89+
90+
Spacer(modifier = Modifier.height(24.dp))
91+
92+
Column(
93+
modifier = Modifier.fillMaxWidth(),
94+
verticalArrangement = Arrangement.spacedBy(28.dp),
95+
) {
96+
ReportDetailLabeledContent(
97+
label = "제목",
98+
content = state.reportTitle,
99+
)
100+
101+
ReportDetailLabeledContent(
102+
label = "카테고리",
103+
content = state.reportCategory.title,
104+
)
105+
106+
ReportDetailLabeledContent(
107+
label = "상세 제목 내용",
108+
content = state.reportContent,
109+
)
110+
111+
ReportDetailLabeledContent(
112+
label = "내 위치",
113+
content = state.location,
114+
)
115+
}
116+
117+
Spacer(modifier = Modifier.height(36.dp))
118+
}
119+
}
120+
121+
@Composable
122+
@Preview
123+
private fun ReportDetailScreenPreview() {
124+
BitnagilTheme {
125+
ReportDetailScreen(
126+
state = ReportDetailState.Init.copy(
127+
reportContent = "Lorem ipsum dolor sit amet, " +
128+
"consectetur adipiscing elit," +
129+
" sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
130+
"Nisl tincidunt eget nullam non.",
131+
),
132+
onClickPreviousButton = {},
133+
)
134+
}
135+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.threegap.bitnagil.presentation.reportdetail.component.atom
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.foundation.shape.RoundedCornerShape
8+
import androidx.compose.material3.Text
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.graphics.Color
12+
import androidx.compose.ui.tooling.preview.Preview
13+
import androidx.compose.ui.unit.dp
14+
import com.threegap.bitnagil.designsystem.BitnagilTheme
15+
import com.threegap.bitnagil.presentation.reportdetail.model.ReportProcess
16+
17+
@Composable
18+
fun ReportProcessBadge(
19+
modifier: Modifier = Modifier,
20+
reportProcess: ReportProcess,
21+
) {
22+
Text(
23+
text = reportProcess.title,
24+
style = BitnagilTheme.typography.caption1SemiBold,
25+
color = reportProcess.getProcessBadgeTextColor(),
26+
modifier = modifier
27+
.background(color = reportProcess.getProcessBadgeBackgroundColor(), shape = RoundedCornerShape(6.dp))
28+
.padding(horizontal = 10.dp, vertical = 4.dp),
29+
)
30+
}
31+
32+
@Composable
33+
private fun ReportProcess.getProcessBadgeBackgroundColor(): Color =
34+
when (this) {
35+
ReportProcess.Reported -> BitnagilTheme.colors.green10
36+
ReportProcess.Progress -> BitnagilTheme.colors.skyBlue10
37+
else -> BitnagilTheme.colors.coolGray95
38+
}
39+
40+
@Composable
41+
private fun ReportProcess.getProcessBadgeTextColor(): Color =
42+
when (this) {
43+
ReportProcess.Reported -> BitnagilTheme.colors.green300
44+
ReportProcess.Progress -> BitnagilTheme.colors.blue300
45+
else -> BitnagilTheme.colors.coolGray40
46+
}
47+
48+
@Composable
49+
@Preview
50+
private fun ReportProcessBadgePreview() {
51+
BitnagilTheme {
52+
Column(
53+
verticalArrangement = Arrangement.spacedBy(4.dp),
54+
) {
55+
ReportProcessBadge(reportProcess = ReportProcess.Progress)
56+
ReportProcessBadge(reportProcess = ReportProcess.Reported)
57+
ReportProcessBadge(reportProcess = ReportProcess.Complete)
58+
}
59+
}
60+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.threegap.bitnagil.presentation.reportdetail.component.block
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Spacer
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.height
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.foundation.shape.RoundedCornerShape
10+
import androidx.compose.material3.Text
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.ui.Modifier
13+
import androidx.compose.ui.unit.dp
14+
import com.threegap.bitnagil.designsystem.BitnagilTheme
15+
16+
@Composable
17+
fun ReportDetailLabeledContent(
18+
modifier: Modifier = Modifier,
19+
label: String,
20+
content: String,
21+
) {
22+
Column(
23+
modifier = modifier,
24+
) {
25+
Text(
26+
text = label,
27+
style = BitnagilTheme.typography.body2SemiBold,
28+
color = BitnagilTheme.colors.coolGray10,
29+
)
30+
31+
Spacer(modifier = Modifier.height(8.dp))
32+
33+
Text(
34+
modifier = Modifier
35+
.fillMaxWidth()
36+
.background(color = BitnagilTheme.colors.coolGray99, shape = RoundedCornerShape(12.dp))
37+
.padding(vertical = 16.dp, horizontal = 20.dp),
38+
text = content,
39+
style = BitnagilTheme.typography.body2Medium,
40+
)
41+
}
42+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.threegap.bitnagil.presentation.reportdetail.model
2+
import com.threegap.bitnagil.domain.report.model.ReportCategory as DomainReportCategory
3+
4+
enum class ReportCategory(
5+
val title: String,
6+
) {
7+
TrafficFacilities(
8+
title = "교통 시설",
9+
),
10+
LightingFacilities(
11+
title = "조명 시설",
12+
),
13+
WaterFacilities(
14+
title = "상하수도 시설",
15+
),
16+
Amenities(
17+
title = "편의 시설",
18+
),
19+
;
20+
21+
companion object {
22+
fun fromDomain(domainReportCategory: com.threegap.bitnagil.domain.report.model.ReportCategory): ReportCategory {
23+
return when (domainReportCategory) {
24+
DomainReportCategory.TRANSPORTATION -> TrafficFacilities
25+
DomainReportCategory.LIGHTING -> LightingFacilities
26+
DomainReportCategory.WATERFACILITY -> WaterFacilities
27+
DomainReportCategory.AMENITY -> Amenities
28+
}
29+
}
30+
}
31+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.threegap.bitnagil.presentation.reportdetail.model
2+
3+
import com.threegap.bitnagil.domain.report.model.ReportStatus
4+
5+
enum class ReportProcess(
6+
val title: String,
7+
) {
8+
Reported(title = "제보 완료"),
9+
Progress(title = "처리 중"),
10+
Complete(title = "처리 완료"),
11+
;
12+
13+
companion object {
14+
fun fromDomain(status: ReportStatus): ReportProcess {
15+
return when (status) {
16+
ReportStatus.Pending -> Reported
17+
ReportStatus.InProgress -> Progress
18+
ReportStatus.Completed -> Complete
19+
}
20+
}
21+
}
22+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.threegap.bitnagil.presentation.reportdetail.model.mvi
2+
3+
interface ReportDetailSideEffect
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.threegap.bitnagil.presentation.reportdetail.model.mvi
2+
3+
import com.threegap.bitnagil.presentation.reportdetail.model.ReportCategory
4+
import com.threegap.bitnagil.presentation.reportdetail.model.ReportProcess
5+
import java.time.LocalDate
6+
7+
data class ReportDetailState(
8+
val reportProcess: ReportProcess,
9+
val reportTitle: String,
10+
val reportContent: String,
11+
val reportCategory: ReportCategory,
12+
val imageUrls: List<String>,
13+
val location: String,
14+
val date: LocalDate,
15+
) {
16+
companion object {
17+
val Init = ReportDetailState(
18+
reportProcess = ReportProcess.Reported,
19+
reportTitle = "",
20+
reportContent = "",
21+
reportCategory = ReportCategory.TrafficFacilities,
22+
imageUrls = emptyList(),
23+
location = "",
24+
date = LocalDate.now(),
25+
)
26+
}
27+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.threegap.bitnagil.presentation.reportdetail.model.navarg
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
data class ReportDetailScreenArg(
7+
val reportId: Int,
8+
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.threegap.bitnagil.presentation.reportdetail.util
2+
3+
import java.time.LocalDate
4+
import java.time.format.DateTimeFormatter
5+
import java.util.Locale
6+
7+
fun LocalDate.toPresentationFormatInReportDetail(): String {
8+
val formatter = DateTimeFormatter.ofPattern("yy.MM.dd (E)", Locale.KOREAN)
9+
return this.format(formatter)
10+
}

0 commit comments

Comments
 (0)