diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 6dfe38b..9ccbfc2 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -79,6 +79,9 @@ dependencies {
implementation(libs.converter.gson)
implementation(libs.androidx.core.splashscreen)
+ implementation(libs.androidx.material)
+ implementation(libs.androidx.material.icons.core)
+ implementation(libs.androidx.material.icons.extended)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
index 4007c41..abf6dd4 100644
Binary files a/app/src/main/ic_launcher-playstore.png and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt b/app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt
index da7e9d9..3368d4b 100644
--- a/app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt
+++ b/app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt
@@ -1,11 +1,7 @@
package com.sampoom.android.app.navigation
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
+import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
@@ -14,7 +10,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavHostController
@@ -30,23 +25,25 @@ const val ROUTE_LOGIN = "login"
const val ROUTE_HOME = "home"
// Main Screen
-const val ROUTE_PART = "part"
-const val ROUTE_INVENTORY = "inventory"
-const val ROUTE_PROFILE = "profile"
-const val ROUTE_SETTINGS = "settings"
+const val ROUTE_DASHBOARD = "dashboard"
+const val ROUTE_DELIVERY = "delivery"
+const val ROUTE_CART = "cart"
+const val ROUTE_ORDERS = "orders"
// Detail Screen
-const val ROUTE_DETAIL = "detail"
+const val ROUTE_PARTS = "parts"
+const val ROUTE_EMPLOYEE = "employee"
+const val ROUTE_SETTINGS = "settings"
sealed class BottomNavItem(
val route: String,
val title: Int,
val icon: Int
) {
- object Part : BottomNavItem(ROUTE_PART, R.string.nav_part, R.drawable.outline_home_24)
- object Inventory : BottomNavItem(ROUTE_INVENTORY, R.string.nav_inventory, R.drawable.outline_home_24)
- object Profile : BottomNavItem(ROUTE_PROFILE, R.string.nav_profile, R.drawable.outline_home_24)
- object Settings : BottomNavItem(ROUTE_SETTINGS, R.string.nav_setting, R.drawable.outline_home_24)
+ object Dashboard : BottomNavItem(ROUTE_DASHBOARD, R.string.nav_dashboard, R.drawable.dashboard)
+ object Delivery : BottomNavItem(ROUTE_DELIVERY, R.string.nav_delivery, R.drawable.delivery)
+ object Cart : BottomNavItem(ROUTE_CART, R.string.nav_cart, R.drawable.cart)
+ object Orders : BottomNavItem(ROUTE_ORDERS, R.string.nav_order, R.drawable.orders)
}
@Composable
@@ -68,7 +65,7 @@ fun AppNavHost() {
})
}
composable(ROUTE_HOME) { MainScreen(navController) }
- composable(ROUTE_DETAIL) { DetailScreen() }
+ composable(ROUTE_PARTS) { PartScreen() }
}
}
@@ -79,30 +76,46 @@ fun MainScreen(
val navController = rememberNavController()
Scaffold(
- bottomBar = {
- BottomNavigationBar(navController)
- }
+ floatingActionButton = { PartsFab(parentNavController) },
+ bottomBar = { BottomNavigationBar(navController) }
) { innerPadding ->
NavHost(
navController = navController,
- startDestination = ROUTE_PART,
- modifier = Modifier.background(Color.Green).padding(innerPadding)
+ startDestination = ROUTE_DASHBOARD,
+ modifier = Modifier.padding(innerPadding)
) {
- composable(ROUTE_PART) { PartScreen() }
- composable(ROUTE_INVENTORY) { InventoryScreen() }
- composable(ROUTE_PROFILE) { ProfileScreen() }
- composable(ROUTE_SETTINGS) { SettingsScreen(parentNavController) }
+ composable(ROUTE_DASHBOARD) { DashboardScreen() }
+ composable(ROUTE_DELIVERY) { DeliveryScreen() }
+ composable(ROUTE_CART) { CartScreen() }
+ composable(ROUTE_ORDERS) { OrderScreen() }
+ }
+ }
+}
+
+@Composable
+fun PartsFab(navController: NavHostController) {
+ FloatingActionButton(
+ onClick = {
+ navController.navigate(ROUTE_PARTS) {
+ popUpTo(navController.graph.startDestinationId) {
+ saveState = true
+ }
+ launchSingleTop = true
+ restoreState = true
+ }
}
+ ) {
+ Icon(painterResource(R.drawable.parts), contentDescription = stringResource(R.string.part_title))
}
}
@Composable
fun BottomNavigationBar(navController: NavHostController) {
val bottomNavItems = listOf(
- BottomNavItem.Part,
- BottomNavItem.Inventory,
- BottomNavItem.Profile,
- BottomNavItem.Settings,
+ BottomNavItem.Dashboard,
+ BottomNavItem.Delivery,
+ BottomNavItem.Cart,
+ BottomNavItem.Orders,
)
NavigationBar {
@@ -130,41 +143,25 @@ fun BottomNavigationBar(navController: NavHostController) {
// 임시 화면들 (실제로는 각각의 feature 모듈에서 구현)
@Composable
-private fun InventoryScreen() {
+private fun DashboardScreen() {
// 홈 화면 구현
- Text("인벤토리 화면")
+ Text("대시보드 화면")
}
@Composable
-private fun ProfileScreen() {
+private fun DeliveryScreen() {
// 프로필 화면 구현
- Text("프로필 화면")
+ Text("Delivery 화면")
}
@Composable
-private fun SettingsScreen(
- navController: NavHostController
-) {
- Column(
- modifier = Modifier.fillMaxSize()
- ) {
- // 설정 화면 구현
- Text("설정 화면")
- Button(
- onClick = { navController.navigate(ROUTE_DETAIL) }
- ) {
- Text("상세 화면")
- }
- }
+private fun CartScreen() {
+ // 프로필 화면 구현
+ Text("Cart 화면")
}
@Composable
-private fun DetailScreen() {
- // 설정 화면 구현
- Scaffold { innerPadding ->
- Box(Modifier.fillMaxSize().background(Color.Red).padding(innerPadding)
- ) {
- Text("상세 화면")
- }
- }
+private fun OrderScreen() {
+ // 프로필 화면 구현
+ Text("Order 화면")
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sampoom/android/core/ui/component/.gitkeep b/app/src/main/java/com/sampoom/android/core/ui/component/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt b/app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt
new file mode 100644
index 0000000..a9ba4d0
--- /dev/null
+++ b/app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt
@@ -0,0 +1,256 @@
+package com.sampoom.android.core.ui.component
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.FilledTonalButton
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+/**
+ * Sampoom common button with multiple visual variants.
+ *
+ * Usage
+ * -----
+ * CommonButton(
+ * text = "Button",
+ * variant = ButtonVariant.Primary,
+ * onClick = { ... }
+ * )
+ *
+ * Optionally pass a leading icon:
+ * CommonButton(
+ * text = "Button",
+ * variant = ButtonVariant.Primary,
+ * leadingIcon = { Icon(painterResource(R.drawable.parts), contentDescription = null) },
+ * onClick = { ... }
+ * )
+ */
+@Composable
+fun CommonButton(
+ text: String,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ variant: ButtonVariant = ButtonVariant.Primary,
+ size: ButtonSize = ButtonSize.Large,
+ leadingIcon: (@Composable (() -> Unit))? = null,
+ onClick: () -> Unit
+) {
+ val cs = MaterialTheme.colorScheme
+ val shape = MaterialTheme.shapes.large
+ val height = when (size) {
+ ButtonSize.Large -> 56.dp
+ ButtonSize.Medium -> 48.dp
+ ButtonSize.Small -> 40.dp
+ }
+
+ when (variant) {
+ ButtonVariant.Primary -> {
+ Button(
+ onClick = onClick,
+ enabled = enabled,
+ shape = shape,
+ modifier = modifier.height(height),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = cs.primary,
+ contentColor = cs.onPrimary,
+ disabledContainerColor = cs.onSurface.copy(alpha = 0.12f),
+ disabledContentColor = cs.onSurface.copy(alpha = 0.38f),
+ )
+ ) {
+ if (leadingIcon != null) {
+ leadingIcon()
+ }
+ Text(
+ text = text,
+ style = MaterialTheme.typography.labelLarge,
+ fontWeight = FontWeight.SemiBold,
+ modifier = Modifier.padding(start = if (leadingIcon != null) 8.dp else 0.dp)
+ )
+ }
+ }
+
+ // Light/secondary (tonal) filled button
+ ButtonVariant.Secondary -> {
+ FilledTonalButton(
+ onClick = onClick,
+ enabled = enabled,
+ shape = shape,
+ modifier = modifier.height(height),
+ colors = ButtonDefaults.filledTonalButtonColors(
+ containerColor = cs.secondaryContainer,
+ contentColor = cs.onSecondaryContainer,
+ disabledContainerColor = cs.onSurface.copy(alpha = 0.08f),
+ disabledContentColor = cs.onSurface.copy(alpha = 0.38f)
+ )
+ ) {
+ if (leadingIcon != null) {
+ leadingIcon()
+ }
+ Text(
+ text = text,
+ style = MaterialTheme.typography.labelLarge,
+ fontWeight = FontWeight.SemiBold,
+ modifier = Modifier.padding(start = if (leadingIcon != null) 8.dp else 0.dp)
+ )
+ }
+ }
+
+ // Outlined with primary border
+ ButtonVariant.Outlined -> {
+ OutlinedButton(
+ onClick = onClick,
+ enabled = enabled,
+ shape = shape,
+ modifier = modifier.height(height),
+ border = BorderStroke(1.dp, cs.primary),
+ colors = ButtonDefaults.outlinedButtonColors(
+ contentColor = cs.primary,
+ disabledContentColor = cs.onSurface.copy(alpha = 0.38f)
+ )
+ ) {
+ Text(
+ text = text,
+ style = MaterialTheme.typography.labelLarge,
+ fontWeight = FontWeight.SemiBold
+ )
+ }
+ }
+
+ // Ghost: no container, subtle onSurface content
+ ButtonVariant.Ghost -> {
+ TextButton(
+ onClick = onClick,
+ enabled = enabled,
+ shape = shape,
+ modifier = modifier.height(height),
+ colors = ButtonDefaults.textButtonColors(
+ contentColor = cs.onSurface,
+ disabledContentColor = cs.onSurface.copy(alpha = 0.38f)
+ )
+ ) {
+ Text(
+ text = text,
+ style = MaterialTheme.typography.labelLarge,
+ fontWeight = FontWeight.SemiBold
+ )
+ }
+ }
+
+ // Destructive/Neutral (dark) filled – matches the black fill example
+ ButtonVariant.Neutral -> {
+ Button(
+ onClick = onClick,
+ enabled = enabled,
+ shape = shape,
+ modifier = modifier.height(height),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = Color(0xFF000000),
+ contentColor = Color.White,
+ disabledContainerColor = cs.onSurface.copy(alpha = 0.12f),
+ disabledContentColor = cs.onSurface.copy(alpha = 0.38f),
+ )
+ ) {
+ Text(
+ text = text,
+ style = MaterialTheme.typography.labelLarge,
+ fontWeight = FontWeight.SemiBold
+ )
+ }
+ }
+ }
+}
+
+enum class ButtonVariant {
+ /** Primary filled (purple in design). Accepts optional [leadingIcon]. */
+ Primary,
+
+ /** Filled tonal (light purple in design). */
+ Secondary,
+
+ /** Outlined with primary border. */
+ Outlined,
+
+ /** No container; text only. */
+ Ghost,
+
+ /** Solid dark/neutral fill. */
+ Neutral,
+}
+
+enum class ButtonSize { Large, Medium, Small }
+
+@Preview(showBackground = true, backgroundColor = 0xFF0F0F10)
+@Composable
+private fun CommonButtonPreview_All() {
+ // Primary
+ CommonButton(
+ text = "Button",
+ variant = ButtonVariant.Primary,
+ onClick = {}
+ )
+}
+
+@Preview(showBackground = true, backgroundColor = 0xFF0F0F10)
+@Composable
+private fun CommonButtonPreview_Primary_WithIcon() {
+ CommonButton(
+ text = "Button",
+ variant = ButtonVariant.Primary,
+ leadingIcon = { Icon(painterResource(android.R.drawable.ic_menu_call), contentDescription = null) },
+ onClick = {}
+ )
+}
+
+@Preview(showBackground = true, backgroundColor = 0xFF0F0F10)
+@Composable
+private fun CommonButtonPreview_Tonal() {
+ CommonButton(
+ text = "Button",
+ variant = ButtonVariant.Secondary,
+ onClick = {}
+ )
+}
+
+@Preview(showBackground = true, backgroundColor = 0xFF0F0F10)
+@Composable
+private fun CommonButtonPreview_Outlined() {
+ CommonButton(
+ text = "Button",
+ variant = ButtonVariant.Outlined,
+ onClick = {}
+ )
+}
+
+@Preview(showBackground = true, backgroundColor = 0xFF0F0F10)
+@Composable
+private fun CommonButtonPreview_Ghost() {
+ CommonButton(
+ text = "Button",
+ variant = ButtonVariant.Ghost,
+ onClick = {}
+ )
+}
+
+@Preview(showBackground = true, backgroundColor = 0xFF0F0F10)
+@Composable
+private fun CommonButtonPreview_Neutral_Disabled() {
+ CommonButton(
+ text = "Button",
+ variant = ButtonVariant.Neutral,
+ enabled = false,
+ onClick = {}
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/sampoom/android/core/ui/component/CommonTextField.kt b/app/src/main/java/com/sampoom/android/core/ui/component/CommonTextField.kt
new file mode 100644
index 0000000..52ba4bf
--- /dev/null
+++ b/app/src/main/java/com/sampoom/android/core/ui/component/CommonTextField.kt
@@ -0,0 +1,164 @@
+package com.sampoom.android.core.ui.component
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Visibility
+import androidx.compose.material.icons.filled.VisibilityOff
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+enum class TextFieldVariant { Outlined, Filled }
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun CommonTextField(
+ value: String,
+ onValueChange: (String) -> Unit,
+ label: String,
+ placeholder: String,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ isPassword: Boolean = false,
+ variant: TextFieldVariant = TextFieldVariant.Outlined
+) {
+ var passwordVisible by remember { mutableStateOf(false) }
+ val darkTheme = isSystemInDarkTheme()
+ val cs = MaterialTheme.colorScheme
+
+ val textColor = if (darkTheme) Color.White else Color.Black
+ val containerColor = if (variant == TextFieldVariant.Filled) {
+ if (darkTheme) Color(0xFF1C1C1E) else Color(0xFFF3F3F3)
+ } else Color.Transparent
+
+ val focusedBorderColor = cs.primary
+ val unfocusedBorderColor = if (darkTheme) Color(0xFF666666) else Color(0xFFCCCCCC)
+
+ val trailingIconView = if (isPassword) {
+ @Composable {
+ IconButton(onClick = { passwordVisible = !passwordVisible }) {
+ Icon(
+ imageVector = if (passwordVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff,
+ contentDescription = null,
+ tint = if (darkTheme) Color.White else Color.Black
+ )
+ }
+ }
+ } else null
+
+ when (variant) {
+ TextFieldVariant.Outlined -> {
+ OutlinedTextField(
+ value = value,
+ onValueChange = onValueChange,
+ label = { Text(text = label, color = textColor) },
+ placeholder = { Text(text = placeholder, color = textColor.copy(alpha = 0.4f)) },
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(vertical = 4.dp),
+ singleLine = true,
+ enabled = enabled,
+ trailingIcon = trailingIconView,
+ visualTransformation = if (isPassword && !passwordVisible) PasswordVisualTransformation() else VisualTransformation.None,
+ keyboardOptions = KeyboardOptions(keyboardType = if (isPassword) KeyboardType.Password else KeyboardType.Text),
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedBorderColor = focusedBorderColor,
+ unfocusedBorderColor = unfocusedBorderColor,
+ disabledBorderColor = Color.Gray,
+ focusedLabelColor = focusedBorderColor,
+ unfocusedLabelColor = textColor.copy(alpha = 0.7f),
+ cursorColor = focusedBorderColor,
+ focusedTextColor = textColor,
+ unfocusedTextColor = textColor
+ ),
+ shape = MaterialTheme.shapes.medium
+ )
+ }
+
+ TextFieldVariant.Filled -> {
+ TextField(
+ value = value,
+ onValueChange = onValueChange,
+ label = { Text(text = label, color = textColor) },
+ placeholder = { Text(text = placeholder, color = textColor.copy(alpha = 0.4f)) },
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(vertical = 4.dp),
+ singleLine = true,
+ enabled = enabled,
+ trailingIcon = trailingIconView,
+ visualTransformation = if (isPassword && !passwordVisible) PasswordVisualTransformation() else VisualTransformation.None,
+ keyboardOptions = KeyboardOptions(keyboardType = if (isPassword) KeyboardType.Password else KeyboardType.Text),
+ colors = TextFieldDefaults.colors(
+ focusedContainerColor = containerColor,
+ unfocusedContainerColor = containerColor,
+ disabledContainerColor = containerColor.copy(alpha = 0.5f),
+ focusedIndicatorColor = focusedBorderColor,
+ unfocusedIndicatorColor = unfocusedBorderColor,
+ cursorColor = focusedBorderColor,
+ focusedTextColor = textColor,
+ unfocusedTextColor = textColor
+ ),
+ shape = MaterialTheme.shapes.medium
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+fun Preview_Light_CommonTextFields() {
+ MaterialTheme {
+ Column {
+ CommonTextField(
+ value = "Example@naver.com",
+ onValueChange = {},
+ label = "이메일 입력",
+ placeholder = "Example@naver.com",
+ variant = TextFieldVariant.Outlined
+ )
+ CommonTextField(
+ value = "",
+ onValueChange = {},
+ label = "비밀번호 입력",
+ placeholder = "비밀번호 입력",
+ isPassword = true,
+ variant = TextFieldVariant.Outlined
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+fun Preview_Dark_CommonTextFields() {
+ MaterialTheme(colorScheme = darkColorScheme()) {
+ Column {
+ CommonTextField(
+ value = "Example@naver.com",
+ onValueChange = {},
+ label = "이메일 입력",
+ placeholder = "Example@naver.com",
+ variant = TextFieldVariant.Filled
+ )
+ CommonTextField(
+ value = "",
+ onValueChange = {},
+ label = "비밀번호 입력",
+ placeholder = "비밀번호 입력",
+ isPassword = true,
+ variant = TextFieldVariant.Filled
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/sampoom/android/feature/part/ui/PartScreen.kt b/app/src/main/java/com/sampoom/android/feature/part/ui/PartScreen.kt
index 2f48b78..1164887 100644
--- a/app/src/main/java/com/sampoom/android/feature/part/ui/PartScreen.kt
+++ b/app/src/main/java/com/sampoom/android/feature/part/ui/PartScreen.kt
@@ -20,60 +20,63 @@ fun PartScreen(
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
- Column(
- modifier = Modifier
- .fillMaxSize()
- .padding(16.dp)
- ) {
- Text(
- text = stringResource(R.string.part_title),
- style = MaterialTheme.typography.headlineMedium,
- fontWeight = FontWeight.Bold,
- modifier = Modifier.padding(bottom = 16.dp)
- )
+ Scaffold { innerPadding ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(innerPadding)
+ .padding(16.dp)
+ ) {
+ Text(
+ text = stringResource(R.string.part_title),
+ style = MaterialTheme.typography.headlineMedium,
+ fontWeight = FontWeight.Bold,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
- when {
- uiState.loading -> {
- Box(
- modifier = Modifier.fillMaxSize(),
- contentAlignment = Alignment.Center
- ) {
- CircularProgressIndicator()
+ when {
+ uiState.loading -> {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator()
+ }
}
- }
-
- uiState.error != null -> {
- Column(
- modifier = Modifier.fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center
- ) {
- Text(
- text = "${stringResource(R.string.common_error)}: ${uiState.error}",
- color = MaterialTheme.colorScheme.error
- )
- Spacer(modifier = Modifier.height(16.dp))
- Button(onClick = { viewModel.refreshPart() }) {
- Text(stringResource((R.string.common_retry)))
+
+ uiState.error != null -> {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text = "${stringResource(R.string.common_error)}: ${uiState.error}",
+ color = MaterialTheme.colorScheme.error
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Button(onClick = { viewModel.refreshPart() }) {
+ Text(stringResource((R.string.common_retry)))
+ }
}
}
- }
-
- uiState.partList.isEmpty() -> {
- Box(
- modifier = Modifier.fillMaxSize(),
- contentAlignment = Alignment.Center
- ) {
- Text(stringResource(R.string.part_empty))
+
+ uiState.partList.isEmpty() -> {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(stringResource(R.string.part_empty))
+ }
}
- }
-
- else -> {
- LazyColumn(
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- items(uiState.partList) { inventory ->
- PartItemCard(part = inventory)
+
+ else -> {
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ items(uiState.partList) { inventory ->
+ PartItemCard(part = inventory)
+ }
}
}
}
diff --git a/app/src/main/res/drawable-night/ic_launcher_foreground.xml b/app/src/main/res/drawable-night/ic_launcher_foreground.xml
index a41ec78..60f045c 100644
--- a/app/src/main/res/drawable-night/ic_launcher_foreground.xml
+++ b/app/src/main/res/drawable-night/ic_launcher_foreground.xml
@@ -11,10 +11,10 @@
android:pathData="M0,0h512v512h-512z"
android:fillColor="#36393F"/>
diff --git a/app/src/main/res/drawable/cart.xml b/app/src/main/res/drawable/cart.xml
new file mode 100644
index 0000000..afc4912
--- /dev/null
+++ b/app/src/main/res/drawable/cart.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/dashboard.xml b/app/src/main/res/drawable/dashboard.xml
new file mode 100644
index 0000000..c2c0e05
--- /dev/null
+++ b/app/src/main/res/drawable/dashboard.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/delivery.xml b/app/src/main/res/drawable/delivery.xml
new file mode 100644
index 0000000..ca411d4
--- /dev/null
+++ b/app/src/main/res/drawable/delivery.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/employee.xml b/app/src/main/res/drawable/employee.xml
new file mode 100644
index 0000000..8ac942e
--- /dev/null
+++ b/app/src/main/res/drawable/employee.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
index 1c28491..078cfa1 100644
--- a/app/src/main/res/drawable/ic_launcher_foreground.xml
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -11,10 +11,10 @@
android:pathData="M0,0h512v512h-512z"
android:fillColor="#ffffff"/>
diff --git a/app/src/main/res/drawable/orders.xml b/app/src/main/res/drawable/orders.xml
new file mode 100644
index 0000000..7521789
--- /dev/null
+++ b/app/src/main/res/drawable/orders.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/parts.xml b/app/src/main/res/drawable/parts.xml
new file mode 100644
index 0000000..ff89964
--- /dev/null
+++ b/app/src/main/res/drawable/parts.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/search.xml b/app/src/main/res/drawable/search.xml
new file mode 100644
index 0000000..519d2d6
--- /dev/null
+++ b/app/src/main/res/drawable/search.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/settings.xml b/app/src/main/res/drawable/settings.xml
new file mode 100644
index 0000000..20ed042
--- /dev/null
+++ b/app/src/main/res/drawable/settings.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
index 7f4c78a..1ed8d22 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
index 09b2c83..46762f5 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
index 719851c..371dc79 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
index 46f6e2c..4898eeb 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
index 75b8c6f..5b3a93f 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
index d92ac47..e963f60 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
index 188e558..0e089a9 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
index dbf8648..7581993 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
index 7b5e294..71adf05 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
index 61466d1..c436d4d 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9ebea0a..8a5d69e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2,10 +2,10 @@
삼품관리
- 부품 조회
- 인벤토리
- 프로필
- 설정
+ 대시보드
+ 출고목록
+ 장바구니
+ 주문관리
로그인
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 14f873b..3c44195 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -10,7 +10,9 @@ junitVersion = "1.3.0"
espressoCore = "3.7.0"
lifecycleRuntimeKtx = "2.9.4"
activityCompose = "1.11.0"
-composeBom = "2025.09.01"
+composeBom = "2025.10.00"
+material = "1.9.3"
+materialIconsCore = "1.7.8"
navigationCompose = "2.9.5"
retrofitVersion = "3.0.0"
@@ -20,6 +22,9 @@ androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", versi
androidx-hilt-lifecycle-viewmodel-compose = { module = "androidx.hilt:hilt-lifecycle-viewmodel-compose", version.ref = "hiltNavigationCompose" }
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
+androidx-material = { module = "androidx.compose.material:material", version.ref = "material" }
+androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core", version.ref = "materialIconsCore" }
+androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofitVersion" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" }