Skip to content
66 changes: 52 additions & 14 deletions app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.sampoom.android.app.navigation

import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
Expand All @@ -23,6 +26,8 @@ import com.sampoom.android.R
import com.sampoom.android.feature.auth.ui.LoginScreen
import com.sampoom.android.feature.auth.ui.SignUpScreen
import com.sampoom.android.feature.cart.ui.CartListScreen
import com.sampoom.android.feature.order.ui.OrderDetailScreen
import com.sampoom.android.feature.order.ui.OrderListScreen
import com.sampoom.android.feature.outbound.ui.OutboundListScreen
import com.sampoom.android.feature.part.ui.PartListScreen
import com.sampoom.android.feature.part.ui.PartScreen
Expand All @@ -41,6 +46,8 @@ const val ROUTE_ORDERS = "orders"
const val ROUTE_PARTS = "parts"
const val ROUTE_PART_LIST = "parts/{agencyId}/group/{groupId}"
fun routePartList(agencyId: Long, groupId: Long): String = "parts/$agencyId/group/$groupId"
const val ROUTE_ORDER_DETAIL = "orders/{agencyId}/orders/{orderId}"
fun routeOrderDetail(agencyId: Long, orderId: Long): String = "orders/$agencyId/orders/$orderId"
const val ROUTE_EMPLOYEE = "employee"
const val ROUTE_SETTINGS = "settings"

Expand All @@ -55,6 +62,7 @@ sealed class BottomNavItem(
object Orders : BottomNavItem(ROUTE_ORDERS, R.string.nav_order, R.drawable.orders)
}

@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun AppNavHost() {
val navController = rememberNavController()
Expand Down Expand Up @@ -96,6 +104,7 @@ fun AppNavHost() {
navController.navigateUp()
},
onNavigatePartList = { group ->
// TODO: 실제 사용자의 agencyId 사용
navController.navigate(routePartList(1, group.id))
}
Comment thread
Sangyoon98 marked this conversation as resolved.
)
Expand All @@ -113,9 +122,23 @@ fun AppNavHost() {
}
)
}
composable(
ROUTE_ORDER_DETAIL,
arguments = listOf(
navArgument("agencyId") { type = NavType.LongType },
navArgument("orderId") { type = NavType.LongType }
)
) {
OrderDetailScreen(
onNavigateBack = {
navController.navigateUp()
}
)
}
}
}

@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun MainScreen(
parentNavController: NavHostController
Expand All @@ -128,13 +151,32 @@ fun MainScreen(
) { innerPadding ->
NavHost(
navController = navController,
startDestination = ROUTE_DASHBOARD,
modifier = Modifier.padding(innerPadding)
startDestination = ROUTE_DASHBOARD
) {
composable(ROUTE_DASHBOARD) { DashboardScreen() }
composable(ROUTE_OUTBOUND) { OutboundListScreen() }
composable(ROUTE_CART) { CartListScreen() }
composable(ROUTE_ORDERS) { OrderScreen() }
composable(ROUTE_DASHBOARD) {
DashboardScreen(
paddingValues = innerPadding
)
}
composable(ROUTE_OUTBOUND) {
OutboundListScreen(
paddingValues = innerPadding
)
}
composable(ROUTE_CART) {
CartListScreen(
paddingValues = innerPadding
)
}
composable(ROUTE_ORDERS) {
OrderListScreen(
paddingValues = innerPadding,
onNavigateOrderDetail = { order ->
// TODO: 실제 사용자의 agencyId 사용
parentNavController.navigate(routeOrderDetail(1, order.orderId))
}
)
}
}
}
}
Expand Down Expand Up @@ -198,13 +240,9 @@ fun BottomNavigationBar(navController: NavHostController) {

// 임시 화면들 (실제로는 각각의 feature 모듈에서 구현)
@Composable
private fun DashboardScreen() {
private fun DashboardScreen(
paddingValues: PaddingValues
) {
// 홈 화면 구현
Text("대시보드 화면")
}

@Composable
private fun OrderScreen() {
// 프로필 화면 구현
Text("Order 화면")
Text("대시보드 화면", modifier = Modifier.padding(paddingValues))
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.sampoom.android.core.network

import android.util.Log
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import retrofit2.HttpException
Expand All @@ -12,6 +13,7 @@ data class ApiErrorResponse(
fun Throwable.serverMessageOrNull(): String? {
if (this is HttpException) {
val errorBody = response()?.errorBody()?.string() ?: return null
Log.d("ErrorHandling", "Error body: $errorBody")
return try {
Gson().fromJson(errorBody, ApiErrorResponse::class.java).message
} catch (_: JsonSyntaxException) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.sampoom.android.core.ui.component

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.sampoom.android.R
import com.sampoom.android.core.ui.theme.FailRed
import com.sampoom.android.core.ui.theme.SuccessGreen
import com.sampoom.android.core.ui.theme.WaitYellow
import com.sampoom.android.feature.order.domain.model.OrderStatus

@Composable
fun StatusChip(status: OrderStatus) {
val (text, color) = when (status) {
OrderStatus.PENDING -> stringResource(R.string.order_status_pending) to WaitYellow
OrderStatus.COMPLETED -> stringResource(R.string.order_status_completed) to SuccessGreen
OrderStatus.CANCELED -> stringResource(R.string.order_status_canceled) to FailRed
}

Surface(
shape = RoundedCornerShape(16.dp),
color = color.copy(alpha = 0.2F)
) {
Text(
text = text,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
style = MaterialTheme.typography.bodySmall,
color = color
)
}
}
29 changes: 29 additions & 0 deletions app/src/main/java/com/sampoom/android/core/util/FormatDate.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.sampoom.android.core.util

import android.os.Build
import androidx.annotation.RequiresApi
import java.time.format.DateTimeFormatter

@RequiresApi(Build.VERSION_CODES.O)
fun formatDate(dateString: String): String {
return runCatching {
val out = DateTimeFormatter.ISO_LOCAL_DATE
val hasOffset =
dateString.contains("Z") || (dateString.contains('T') && (dateString.lastIndexOf('+') > dateString.indexOf(
'T'
) || (dateString.lastIndexOf('-') > dateString.indexOf('T') && dateString.count { it == ':' } >= 3)))
val date = if (hasOffset) {
val inFmt = DateTimeFormatter.ISO_OFFSET_DATE_TIME
java.time.OffsetDateTime.parse(dateString, inFmt).toLocalDate()
} else {
val inFmt = java.time.format.DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd'T'HH:mm:ss")
.optionalStart()
.appendFraction(java.time.temporal.ChronoField.NANO_OF_SECOND, 0, 6, true)
.optionalEnd()
.toFormatter(java.util.Locale.ROOT)
java.time.LocalDateTime.parse(dateString, inFmt).toLocalDate()
}
date.format(out)
}.getOrElse { dateString }
}
26 changes: 26 additions & 0 deletions app/src/main/java/com/sampoom/android/core/util/OrderTitle.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.sampoom.android.core.util

import com.sampoom.android.feature.order.domain.model.Order
import com.sampoom.android.feature.order.domain.model.OrderPart

fun buildOrderTitle(order: Order): String {
val flattened: List<Triple<String /*category*/, String /*group*/, OrderPart>> =
order.items.flatMap { category ->
category.groups.flatMap { group ->
group.parts.map { part -> Triple(category.categoryName, group.groupName, part) }
}
}

if (flattened.isEmpty()) return "-"

val first = flattened.first()
val groupName = first.second
val part = first.third
val totalParts = flattened.size

return if (totalParts == 1) {
"$groupName - ${part.name} ${part.quantity}EA"
} else {
"$groupName - ${part.name} ${part.quantity}EA 외 ${totalParts - 1}건"
}
Comment thread
Sangyoon98 marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path

// TODO: AgencyId 동적 주입
interface CartApi {
// 장바구니 목록 조회
@GET("agency/1/cart")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import com.sampoom.android.feature.cart.data.remote.dto.AddCartRequestDto
import com.sampoom.android.feature.cart.data.remote.dto.UpdateCartRequestDto
import com.sampoom.android.feature.cart.domain.model.CartList
import com.sampoom.android.feature.cart.domain.repository.CartRepository
import kotlinx.coroutines.CancellationException
import javax.inject.Inject
import kotlin.Result

class CartRepositoryImpl @Inject constructor(
private val api: CartApi
Expand All @@ -21,33 +23,53 @@ class CartRepositoryImpl @Inject constructor(
partId: Long,
quantity: Long
): Result<Unit> {
val dto = api.addCart(AddCartRequestDto(partId, quantity))
return runCatching {
return try {
val dto = api.addCart(AddCartRequestDto(partId, quantity))
if (!dto.success) throw Exception(dto.message)
Result.success(Unit)
} catch (ce: CancellationException) {
throw ce
} catch (t : Throwable) {
Result.failure(t)
}
}

override suspend fun deleteCart(cartItemId: Long): Result<Unit> {
val dto = api.deleteCart(cartItemId)
return runCatching {
return try {
val dto = api.deleteCart(cartItemId)
if (!dto.success) throw Exception(dto.message)
Result.success(Unit)
} catch (ce: CancellationException) {
throw ce
} catch (t : Throwable) {
Result.failure(t)
}
}

override suspend fun deleteAllCart(): Result<Unit> {
val dto = api.deleteAllCart()
return runCatching {
return try {
val dto = api.deleteAllCart()
if (!dto.success) throw Exception(dto.message)
Result.success(Unit)
} catch (ce: CancellationException) {
throw ce
} catch (t : Throwable) {
Result.failure(t)
}
}

override suspend fun updateCartQuantity(
cartItemId: Long,
quantity: Long
): Result<Unit> {
val dto = api.updateCart(cartItemId, UpdateCartRequestDto(quantity))
return runCatching {
return try {
val dto = api.updateCart(cartItemId, UpdateCartRequestDto(quantity))
if (!dto.success) throw Exception(dto.message)
Result.success(Unit)
} catch (ce: CancellationException) {
throw ce
} catch (t : Throwable) {
Result.failure(t)
}
}
}
Loading