feat: add btn nav
This commit is contained in:
@@ -50,6 +50,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.compose.ui.graphics)
|
implementation(libs.androidx.compose.ui.graphics)
|
||||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
|
implementation(libs.androidx.material3)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
@@ -57,7 +58,7 @@ dependencies {
|
|||||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
|
implementation(libs.androidx.compose.material.icons.core)
|
||||||
// ViewModel
|
// ViewModel
|
||||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||||
implementation(libs.androidx.lifecycle.viewmodel.ktx)
|
implementation(libs.androidx.lifecycle.viewmodel.ktx)
|
||||||
@@ -78,4 +79,7 @@ dependencies {
|
|||||||
// Koin for DI
|
// Koin for DI
|
||||||
implementation(libs.koin.android)
|
implementation(libs.koin.android)
|
||||||
implementation(libs.koin.androidx.compose.v410)
|
implementation(libs.koin.androidx.compose.v410)
|
||||||
|
|
||||||
|
//navigation
|
||||||
|
implementation(libs.androidx.navigation.compose)
|
||||||
}
|
}
|
||||||
@@ -9,14 +9,25 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.approagency.drug.navigation.AppNavGraph
|
||||||
import com.approagency.drug.presentation.screens.HomeScreen
|
import com.approagency.drug.presentation.screens.HomeScreen
|
||||||
import com.approagency.drug.ui.theme.DrugTheme
|
import com.approagency.drug.ui.theme.DrugTheme
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -25,18 +36,15 @@ class MainActivity : ComponentActivity() {
|
|||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
DrugTheme {
|
DrugTheme {
|
||||||
Scaffold(modifier = Modifier.fillMaxSize() , topBar = {
|
val navController = rememberNavController()
|
||||||
TopAppBar(
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
val scope = rememberCoroutineScope()
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
Scaffold(modifier = Modifier.fillMaxSize(),
|
||||||
titleContentColor = MaterialTheme.colorScheme.primary,
|
) { innerPadding ->
|
||||||
),
|
AppNavGraph(
|
||||||
title = {
|
navController = navController,
|
||||||
Text("Small Top App Bar")
|
modifier = Modifier.padding(innerPadding)
|
||||||
}
|
|
||||||
)
|
)
|
||||||
},) { innerPadding ->
|
|
||||||
HomeScreen(modifier = Modifier.padding(innerPadding))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.approagency.drug.data.dto
|
||||||
|
|
||||||
|
data class DarmanModel(
|
||||||
|
val success : String,
|
||||||
|
val message:String,
|
||||||
|
val data: List<DarmanList>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DarmanList(
|
||||||
|
val cod : Int,
|
||||||
|
val nam_fa:String,
|
||||||
|
val nam_en:String,
|
||||||
|
)
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.approagency.drug.data.remote
|
package com.approagency.drug.data.remote
|
||||||
|
|
||||||
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.data.dto.DrugModels
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
@@ -21,4 +22,8 @@ interface DrugApiService {
|
|||||||
suspend fun getDrugDetail(
|
suspend fun getDrugDetail(
|
||||||
@Path("cod") cod: Int
|
@Path("cod") cod: Int
|
||||||
): DrugModels
|
): DrugModels
|
||||||
|
|
||||||
|
|
||||||
|
@GET("drugs/goroh-darmani")
|
||||||
|
suspend fun getGorohDaroei(): DarmanModel
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.approagency.drug.data.repository
|
package com.approagency.drug.data.repository
|
||||||
|
|
||||||
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import com.approagency.drug.data.remote.DrugApiService
|
import com.approagency.drug.data.remote.DrugApiService
|
||||||
import com.approagency.drug.domain.model.DrugSearchParams
|
import com.approagency.drug.domain.model.DrugSearchParams
|
||||||
import com.approagency.drug.domain.repository.DrugRepository
|
import com.approagency.drug.domain.repository.DrugRepository
|
||||||
@@ -16,4 +18,22 @@ class DrugRepositoryImpl(
|
|||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun drugDetail(cod: Int): Result<DrugModels> {
|
||||||
|
return try {
|
||||||
|
val response= apiService.getDrugDetail(cod = cod)
|
||||||
|
return Result.success(response)
|
||||||
|
}catch (e: Exception){
|
||||||
|
Result.failure(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getGorohDaroei(): Result<DarmanModel> {
|
||||||
|
return try {
|
||||||
|
val response = apiService.getGorohDaroei()
|
||||||
|
return Result.success(response)
|
||||||
|
}catch (e: Exception){
|
||||||
|
Result.failure(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,8 @@ import android.app.Application
|
|||||||
import com.approagency.drug.data.remote.DrugApiService
|
import com.approagency.drug.data.remote.DrugApiService
|
||||||
import com.approagency.drug.data.repository.DrugRepositoryImpl
|
import com.approagency.drug.data.repository.DrugRepositoryImpl
|
||||||
import com.approagency.drug.domain.repository.DrugRepository
|
import com.approagency.drug.domain.repository.DrugRepository
|
||||||
|
import com.approagency.drug.domain.usecase.GetDarmanUseCase
|
||||||
|
import com.approagency.drug.domain.usecase.GetDrugDetailUseCase
|
||||||
import com.approagency.drug.domain.usecase.GetDrugSearchUseCase
|
import com.approagency.drug.domain.usecase.GetDrugSearchUseCase
|
||||||
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
||||||
import com.approagency.drug.utils.Config
|
import com.approagency.drug.utils.Config
|
||||||
@@ -34,10 +36,19 @@ val appModule= module {
|
|||||||
//UseCase
|
//UseCase
|
||||||
single {
|
single {
|
||||||
GetDrugSearchUseCase(get())
|
GetDrugSearchUseCase(get())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
single{
|
||||||
|
GetDrugDetailUseCase(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
single {
|
||||||
|
GetDarmanUseCase(get())
|
||||||
}
|
}
|
||||||
|
|
||||||
//view model
|
//view model
|
||||||
viewModel {
|
viewModel {
|
||||||
HomeViewModel(get())
|
HomeViewModel(get() , get() , get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
package com.approagency.drug.domain.repository
|
package com.approagency.drug.domain.repository
|
||||||
|
|
||||||
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import com.approagency.drug.domain.model.DrugSearchParams
|
import com.approagency.drug.domain.model.DrugSearchParams
|
||||||
|
|
||||||
interface DrugRepository {
|
interface DrugRepository {
|
||||||
suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse>
|
suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse>
|
||||||
|
suspend fun drugDetail(cod:Int): Result<DrugModels>
|
||||||
|
suspend fun getGorohDaroei(): Result<DarmanModel>
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
|
import com.approagency.drug.domain.repository.DrugRepository
|
||||||
|
|
||||||
|
class GetDarmanUseCase (
|
||||||
|
private val repository: DrugRepository
|
||||||
|
){
|
||||||
|
suspend operator fun invoke (): Result<DarmanModel>{
|
||||||
|
return repository.getGorohDaroei()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
|
import com.approagency.drug.domain.repository.DrugRepository
|
||||||
|
|
||||||
|
class GetDrugDetailUseCase
|
||||||
|
(
|
||||||
|
private val repository: DrugRepository){
|
||||||
|
suspend operator fun invoke(cod:Int): Result<DrugModels>{
|
||||||
|
return repository.drugDetail(cod)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.approagency.drug.navigation
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import com.approagency.drug.presentation.screens.HomeScreen
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import com.approagency.drug.presentation.screens.RootScreen
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AppNavGraph(navController: NavHostController, modifier: Modifier = Modifier) {
|
||||||
|
NavHost(
|
||||||
|
navController = navController ,
|
||||||
|
startDestination = NavRoutes.Home.route
|
||||||
|
){
|
||||||
|
composable(NavRoutes.Home.route) {
|
||||||
|
RootScreen(
|
||||||
|
navHostController = navController , modifier = Modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.approagency.drug.navigation
|
||||||
|
|
||||||
|
abstract class NavRoutes (val route:String) {
|
||||||
|
object Home: NavRoutes("home")
|
||||||
|
object Detail: NavRoutes("detail/{cod}"){
|
||||||
|
fun createRoute(cod: Int) = "detail/$cod"
|
||||||
|
}
|
||||||
|
object Lab: NavRoutes("lab")
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ fun CustomModalDialog(
|
|||||||
){
|
){
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
|
||||||
.wrapContentHeight(),
|
.wrapContentHeight(),
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
color = MaterialTheme.colorScheme.surface,
|
color = MaterialTheme.colorScheme.surface,
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
package com.approagency.drug.presentation.common
|
package com.approagency.drug.presentation.common
|
||||||
|
|
||||||
import android.content.res.Resources.Theme
|
import android.content.res.Resources.Theme
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Loading() {
|
fun Loading(
|
||||||
|
color: Color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
color = MaterialTheme.colorScheme.onPrimary,
|
color = color,
|
||||||
modifier = Modifier,
|
modifier = modifier,
|
||||||
strokeWidth = 1.dp
|
strokeWidth = 2.dp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.approagency.drug.presentation.common
|
package com.approagency.drug.presentation.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
@@ -8,6 +9,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -29,8 +31,7 @@ fun PrimaryButton(
|
|||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
if (isLoading == true) {
|
if (isLoading == true) {
|
||||||
Loading(
|
Text("... در حال جستجو" , textAlign = TextAlign.Right)
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Text(text)
|
Text(text)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
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.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.AbsoluteAlignment
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.approagency.drug.data.dto.DarmanList
|
||||||
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
|
import com.approagency.drug.data.dto.DrugDetail
|
||||||
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
|
import com.approagency.drug.presentation.common.PrimaryButton
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
import kotlin.collections.emptyList
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DarmanContent(
|
||||||
|
darmanData: Result<DarmanModel?>?,
|
||||||
|
onDarmanClick: (Int) -> Unit,
|
||||||
|
onRetryClick:()-> Unit,
|
||||||
|
){
|
||||||
|
val drugs = darmanData?.getOrNull()?.data ?: emptyList()
|
||||||
|
if (drugs.isEmpty()){
|
||||||
|
CustomBox(
|
||||||
|
child = {
|
||||||
|
Column {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(all = MaterialTheme.dime.sm),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "مشکل در دریافت اطلاعات",
|
||||||
|
fontWeight = FontWeight.W500
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(MaterialTheme.dime.xs))
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Search,
|
||||||
|
contentDescription = "Search"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PrimaryButton(
|
||||||
|
text = "تلاش دوباره",
|
||||||
|
height = 45,
|
||||||
|
isLoading = false,
|
||||||
|
onClick = {
|
||||||
|
onRetryClick()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}else {
|
||||||
|
LazyColumn(
|
||||||
|
|
||||||
|
){
|
||||||
|
items(drugs.count() , itemContent ={
|
||||||
|
i -> DarmanItem(
|
||||||
|
darman = darmanData?.getOrNull()!!.data[i] ,
|
||||||
|
onClick = { onDarmanClick(drugs[i].cod!!) }
|
||||||
|
)
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DarmanItem(
|
||||||
|
darman: DarmanList, // Adjust this based on your actual data class
|
||||||
|
onClick: (Int?) -> Unit
|
||||||
|
) {
|
||||||
|
CustomBox(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(bottom = MaterialTheme.dime.sm)
|
||||||
|
.clickable(
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) {
|
||||||
|
onClick(darman.cod)
|
||||||
|
},
|
||||||
|
child = {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.padding(all = MaterialTheme.dime.lg),
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.Start
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = AbsoluteAlignment.Right
|
||||||
|
// horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(darman.nam_fa.toString() , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right )
|
||||||
|
Text(darman.nam_en.toString() , style = MaterialTheme.typography.bodySmall)
|
||||||
|
}
|
||||||
|
Row (
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onClick(darman.cod)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
|
||||||
|
, contentDescription = "see more"
|
||||||
|
)
|
||||||
|
Text("مشاهده لیست")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// Button(
|
||||||
|
// onClick = onClick,
|
||||||
|
// modifier = Modifier
|
||||||
|
// .fillMaxWidth()
|
||||||
|
// .padding(vertical = 4.dp)
|
||||||
|
// ) {
|
||||||
|
// drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
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.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import com.approagency.drug.data.dto.DrugDetail
|
||||||
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DrugListContent(
|
||||||
|
drugsData: Result<DrugListResponse?>?,
|
||||||
|
onDrugClick: (Int) -> Unit
|
||||||
|
) {
|
||||||
|
val drugs = drugsData?.getOrNull()?.data ?: emptyList()
|
||||||
|
|
||||||
|
if (drugs.isEmpty()) {
|
||||||
|
CustomBox(
|
||||||
|
child = {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(all = MaterialTheme.dime.sm),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "نتیجهای یافت نشد",
|
||||||
|
fontWeight = FontWeight.W500
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(MaterialTheme.dime.xs))
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Search,
|
||||||
|
contentDescription = "Search"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
LazyColumn(
|
||||||
|
|
||||||
|
){
|
||||||
|
items(drugs.count() , itemContent ={
|
||||||
|
i -> DrugItem(
|
||||||
|
drug = drugs[i],
|
||||||
|
onClick = { onDrugClick(drugs[i].cod!!) }
|
||||||
|
)
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DrugItem(
|
||||||
|
drug: DrugDetail, // Adjust this based on your actual data class
|
||||||
|
onClick: (Int?) -> Unit
|
||||||
|
) {
|
||||||
|
CustomBox(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(bottom = MaterialTheme.dime.sm)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) {
|
||||||
|
onClick(drug.cod)
|
||||||
|
},
|
||||||
|
child = {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.padding(all = MaterialTheme.dime.lg),
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.Start
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(drug.nam_en.toString() , style = MaterialTheme.typography.bodySmall)
|
||||||
|
Text(drug.nam_fa.toString() , style = MaterialTheme.typography.labelLarge,textAlign = TextAlign.Right)
|
||||||
|
}
|
||||||
|
if (drug.goroh_darmani != null )
|
||||||
|
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
, horizontalArrangement = Arrangement.End
|
||||||
|
) {Text(
|
||||||
|
"گروه دارویی: ${drug.goroh_darmani.nam_fa.toString()}",
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
textAlign = TextAlign.Right
|
||||||
|
) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Row (
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onClick(drug.cod)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
|
||||||
|
, contentDescription = "see more"
|
||||||
|
)
|
||||||
|
Text("مشاهده جزییات")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// Button(
|
||||||
|
// onClick = onClick,
|
||||||
|
// modifier = Modifier
|
||||||
|
// .fillMaxWidth()
|
||||||
|
// .padding(vertical = 4.dp)
|
||||||
|
// ) {
|
||||||
|
// drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EmptyStateContent() {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
, verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Search,
|
||||||
|
contentDescription = "Search",
|
||||||
|
modifier = Modifier.size(MaterialTheme.dime.iconLg)
|
||||||
|
)
|
||||||
|
CustomBox(
|
||||||
|
child = {
|
||||||
|
Text(
|
||||||
|
text = "برای جستجو، نام دارو را وارد کنید",
|
||||||
|
modifier = Modifier.padding(MaterialTheme.dime.sm)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items // Import items if you were to use it for lists within LazyColumn
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import com.approagency.drug.data.dto.DrugDetail // Import DrugDetail directly
|
||||||
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
|
import com.approagency.drug.presentation.common.CustomModalBottomSheet
|
||||||
|
import com.approagency.drug.presentation.viewModel.DrugDetailState
|
||||||
|
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
||||||
|
import com.vada.caller.ui.theme.dime // Assuming this is your custom spacing/dimension object
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
fun DrugDetailSheet(
|
||||||
|
viewModel: HomeViewModel,
|
||||||
|
state: DrugDetailState // Assuming you pass the state containing the detail
|
||||||
|
) {
|
||||||
|
val detail: DrugDetail? = state.drugDetail?.getOrNull()?.data
|
||||||
|
|
||||||
|
// Only show the sheet if detail is not null
|
||||||
|
if (detail != null) {
|
||||||
|
CustomModalBottomSheet(
|
||||||
|
onDismiss = {
|
||||||
|
viewModel.closeDetail()
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
// Use a LazyColumn for scrollable content inside the sheet
|
||||||
|
|
||||||
|
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = MaterialTheme.dime.lg),
|
||||||
|
contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg) // Add padding to the bottom of the content for better scrolling
|
||||||
|
) {
|
||||||
|
// --- 1. Main Title ---
|
||||||
|
item { // Use item {} for single composables within LazyColumn
|
||||||
|
Text(
|
||||||
|
text = detail.nam_fa ?: "جزئیات دارو",
|
||||||
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.padding(bottom = MaterialTheme.dime.md)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = detail.nam_en ?: "N/A (English Name)",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(bottom = MaterialTheme.dime.lg)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Divider for visual separation
|
||||||
|
HorizontalDivider(Modifier.padding(bottom = MaterialTheme.dime.md))
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 2. Key Groupings ---
|
||||||
|
// Goroh Darmani (Pharmacological Group)
|
||||||
|
detail.goroh_darmani?.let { group ->
|
||||||
|
item {
|
||||||
|
DetailRow(
|
||||||
|
title = "گروه درمانی",
|
||||||
|
value = group.nam_fa ?: "نامشخص"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
group.nam_en?.let { enName ->
|
||||||
|
item {
|
||||||
|
DetailRow(
|
||||||
|
title = "Group (EN)",
|
||||||
|
value = enName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Goroh Darmanei (Therapeutic Group)
|
||||||
|
detail.goroh_daroei?.let { group ->
|
||||||
|
item {
|
||||||
|
DetailRow(
|
||||||
|
title = "گروه دارویی",
|
||||||
|
value = group.nam ?: "نامشخص"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Goroh Farmakologic (Pharmacological Class)
|
||||||
|
detail.goroh_farmakologic_cod?.let { cod ->
|
||||||
|
// NOTE: You might need a separate API call or mapping here
|
||||||
|
// if cod needs to be translated to a name. For now, we show the code.
|
||||||
|
item {
|
||||||
|
DetailRow(
|
||||||
|
title = "کلاس فارماکولوژیک",
|
||||||
|
value = cod.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item { // Add a divider as a separate item
|
||||||
|
HorizontalDivider(Modifier.padding(vertical = MaterialTheme.dime.md))
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 3. Usage & Mechanism ---
|
||||||
|
detail.mavaredmasraf?.let { usage ->
|
||||||
|
item {
|
||||||
|
DetailSection(title = "موارد مصرف", content = usage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detail.mekanismtasir?.let { mechanism ->
|
||||||
|
item {
|
||||||
|
DetailSection(title = "مکانیسم اثر", content = mechanism)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 4. Warnings & Storage ---
|
||||||
|
detail.hoshdar?.let { warning ->
|
||||||
|
item {
|
||||||
|
DetailSection(
|
||||||
|
title = "هشدارها",
|
||||||
|
content = warning,
|
||||||
|
isWarning = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detail.sharayetnegahdari?.let { storage ->
|
||||||
|
item {
|
||||||
|
DetailSection(
|
||||||
|
title = "شرایط نگهداری",
|
||||||
|
content = storage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Helper Composables for Clean Code ---
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DetailRow(
|
||||||
|
title: String,
|
||||||
|
value: String
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = MaterialTheme.dime.sm),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
textAlign = TextAlign.Right
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = value,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
textAlign = TextAlign.End,
|
||||||
|
// Ensure text takes up available space and doesn't push other elements.
|
||||||
|
// Using weight(1f, fill = false) is good for aligning to the end if there's space.
|
||||||
|
modifier = Modifier.weight(1f, fill = false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DetailSection(
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
isWarning: Boolean = false
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = MaterialTheme.dime.md)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.padding(bottom = MaterialTheme.dime.sm)
|
||||||
|
)
|
||||||
|
// Use a text block that respects line breaks (usually done with \n)
|
||||||
|
Text(
|
||||||
|
text = content,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
// Consider adding softWrap = true if content might overflow
|
||||||
|
// softWrap = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Add divider after each section for better visual separation
|
||||||
|
HorizontalDivider(Modifier.padding(top = MaterialTheme.dime.sm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure your DrugDetailState in ViewModel looks something like this:
|
||||||
|
// data class DrugDetailState(
|
||||||
|
// val isLoading: Boolean = false,
|
||||||
|
// val drugDetail: DrugModels? = null, // Changed to DrugModels? to match data access
|
||||||
|
// val isDetailVisible: Boolean = false,
|
||||||
|
// val error: String? = null
|
||||||
|
// )
|
||||||
@@ -1,7 +1,14 @@
|
|||||||
package com.approagency.drug.presentation.screens
|
package com.approagency.drug.presentation.screens
|
||||||
|
|
||||||
|
import android.R
|
||||||
|
import android.R.attr.fontWeight
|
||||||
|
import android.content.res.Resources
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.gestures.snapping.SnapPosition
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -10,18 +17,35 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
||||||
|
import androidx.compose.material.icons.filled.Info
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowLeft
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material.icons.twotone.Info
|
||||||
|
import androidx.compose.material.icons.twotone.KeyboardArrowLeft
|
||||||
|
import androidx.compose.material.icons.twotone.KeyboardArrowRight
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -29,170 +53,240 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.compose.ui.unit.TextUnit
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
import com.approagency.drug.data.dto.DrugDetail
|
import com.approagency.drug.data.dto.DrugDetail
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.domain.model.DrugSearchParams
|
import com.approagency.drug.domain.model.DrugSearchParams
|
||||||
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
|
import com.approagency.drug.presentation.common.CustomModalBottomSheet
|
||||||
import com.approagency.drug.presentation.common.CustomModalDialog
|
import com.approagency.drug.presentation.common.CustomModalDialog
|
||||||
import com.approagency.drug.presentation.common.Loading
|
import com.approagency.drug.presentation.common.Loading
|
||||||
import com.approagency.drug.presentation.common.PrimaryButton
|
import com.approagency.drug.presentation.common.PrimaryButton
|
||||||
import com.approagency.drug.presentation.viewModel.DrugSearchState
|
import com.approagency.drug.presentation.components.DarmanContent
|
||||||
|
import com.approagency.drug.presentation.components.DrugDetailSheet
|
||||||
|
import com.approagency.drug.presentation.components.DrugListContent
|
||||||
|
import com.approagency.drug.presentation.components.EmptyStateContent
|
||||||
|
import com.approagency.drug.presentation.viewModel.CombineState
|
||||||
|
import com.approagency.drug.presentation.viewModel.HomeEvent
|
||||||
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
||||||
|
import com.vada.caller.ui.theme.Dime
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.dime
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(
|
fun HomeScreen(
|
||||||
|
navController : NavController,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
viewModel: HomeViewModel = koinViewModel()
|
viewModel: HomeViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
val state by viewModel.uiState.collectAsState()
|
val state by viewModel.uiState.collectAsState()
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
if (state.drugDetailState.isDetailVisible) {
|
||||||
|
DrugDetailSheet(
|
||||||
|
viewModel = viewModel,
|
||||||
|
state = state.drugDetailState
|
||||||
|
)
|
||||||
|
}
|
||||||
HomeContent(
|
HomeContent(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
state = state,
|
state = state,
|
||||||
onSearch = { value -> viewModel.searchDrugs(DrugSearchParams(query = value)) },
|
viewModelEvent = viewModel.event,
|
||||||
|
onSearch = { value ->
|
||||||
|
if (value is Int) {
|
||||||
|
viewModel.searchDrugs(DrugSearchParams(healGroup = value))
|
||||||
|
} else if (value is String) {
|
||||||
|
viewModel.searchDrugs(DrugSearchParams(query = value))
|
||||||
|
}
|
||||||
|
keyboardController?.hide()
|
||||||
|
focusManager.clearFocus()},
|
||||||
onRetry = {
|
onRetry = {
|
||||||
viewModel.clearError()
|
viewModel.getDarmani()
|
||||||
},
|
},
|
||||||
onDrugClick = { drugId -> println(drugId) }
|
onDrugClick = { drugId -> viewModel.drugDetail(drugId , allDrugs = state.drugSearchState.drugsData) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeContent(
|
fun HomeContent(
|
||||||
state: DrugSearchState,
|
state: CombineState,
|
||||||
onSearch: (String) -> Unit,
|
viewModelEvent: SharedFlow<HomeEvent>,
|
||||||
|
onSearch: (Any) -> Unit,
|
||||||
onRetry: () -> Unit,
|
onRetry: () -> Unit,
|
||||||
onDrugClick: (String) -> Unit,
|
onDrugClick: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
var search by remember { mutableStateOf("") }
|
var search by remember { mutableStateOf("") }
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize().padding(horizontal = MaterialTheme.dime.md)
|
||||||
.padding(16.dp),
|
.padding(top = MaterialTheme.dime.md),
|
||||||
horizontalAlignment = Alignment.Start
|
horizontalAlignment = Alignment.Start,
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
){
|
){
|
||||||
Column {
|
|
||||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||||
OutlinedTextField(
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(45.dp) // Your desired height
|
||||||
|
.background(
|
||||||
|
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
shape =MaterialTheme.shapes.medium
|
||||||
|
)
|
||||||
|
.border(
|
||||||
|
width = 1.dp,
|
||||||
|
color = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
shape =MaterialTheme.shapes.medium
|
||||||
|
)
|
||||||
|
.padding(horizontal = MaterialTheme.dime.md, vertical = MaterialTheme.dime.sm)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
BasicTextField(
|
||||||
value = search,
|
value = search,
|
||||||
shape = MaterialTheme.shapes.large,
|
onValueChange = { search = it },
|
||||||
onValueChange = { value ->
|
modifier = Modifier.weight(1f),
|
||||||
search = value
|
singleLine = true,
|
||||||
},
|
textStyle = MaterialTheme.typography.bodyLarge,
|
||||||
label = { Text("جستجوی دارو") },
|
decorationBox = { innerTextField ->
|
||||||
modifier = Modifier.fillMaxWidth(),
|
Box {
|
||||||
|
if (search.isEmpty()) {
|
||||||
trailingIcon = {
|
Text(
|
||||||
IconButton(onClick = {
|
"جستجوی دارو",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
innerTextField()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
if (search.isNotBlank()) {
|
if (search.isNotBlank()) {
|
||||||
onSearch(search)
|
onSearch(search)
|
||||||
}
|
}
|
||||||
}) {
|
},
|
||||||
|
modifier = Modifier.size(32.dp)
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Search,
|
imageVector = Icons.Default.Search,
|
||||||
contentDescription = "Search"
|
contentDescription = "Search",
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
text = "جستجو",
|
text = "جستجو",
|
||||||
height = 45,
|
height = 40,
|
||||||
isLoading = state.isLoading,
|
isLoading = state.drugSearchState.isLoading,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (search.isNotBlank()) {
|
if (search.isNotBlank()) {
|
||||||
onSearch(search)
|
onSearch(search)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
|
||||||
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
|
|
||||||
when {
|
// Always draw the data if available
|
||||||
state.isLoading ->{
|
if (state.drugSearchState.drugsData != null && !state.showDarmanList) {
|
||||||
CustomModalDialog(
|
|
||||||
onDismissRequest = {
|
|
||||||
|
|
||||||
},
|
|
||||||
content = {
|
|
||||||
Column {
|
Column {
|
||||||
Loading()
|
Row(
|
||||||
Text("در حال جستجو...")
|
horizontalArrangement = Arrangement.End,
|
||||||
}
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onRetry()
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
Text("مشاهده تمامی گروه های دارویی")
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.KeyboardArrowRight,
|
||||||
|
contentDescription = "back"
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.width(MaterialTheme.dime.sm))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
state.drugsData != null ->{
|
|
||||||
DrugListContent(
|
DrugListContent(
|
||||||
drugsData = state.drugsData,
|
drugsData = state.drugSearchState.drugsData,
|
||||||
onDrugClick = onDrugClick
|
onDrugClick = onDrugClick
|
||||||
)
|
)
|
||||||
}else -> {
|
}
|
||||||
|
}
|
||||||
|
else if (state.darmanState.getDarmani != null ){
|
||||||
|
Column {
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.End
|
||||||
|
) {
|
||||||
|
Text("لیست گروه های دارویی" , style = MaterialTheme.typography.labelLarge.copy(
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
) ,modifier= Modifier.padding(MaterialTheme.dime.md))
|
||||||
|
}
|
||||||
|
|
||||||
|
DarmanContent(
|
||||||
|
darmanData = state.darmanState.getDarmani,
|
||||||
|
onDarmanClick = { value ->
|
||||||
|
onSearch(value)
|
||||||
|
},
|
||||||
|
onRetryClick = {
|
||||||
|
onRetry()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
EmptyStateContent()
|
EmptyStateContent()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DrugListContent(
|
|
||||||
drugsData: Result<DrugListResponse?>?,
|
|
||||||
onDrugClick: (String) -> Unit
|
|
||||||
) {
|
|
||||||
val drugs = drugsData?.getOrNull()?.data ?: emptyList()
|
|
||||||
|
|
||||||
if (drugs.isEmpty()) {
|
// Draw the loading dialog on top if needed
|
||||||
Text(
|
if (state.drugSearchState.isLoading || state.drugDetailState.isLoading || state.darmanState.isLoading) {
|
||||||
text = "نتیجهای یافت نشد",
|
CustomModalDialog(
|
||||||
modifier = Modifier.padding(16.dp)
|
onDismissRequest = { /* maybe disable dismiss while loading */ },
|
||||||
)
|
content = {
|
||||||
} else {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
// Display drug list
|
Loading(color = MaterialTheme.colorScheme.primary)
|
||||||
Column {
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
|
||||||
drugs.forEach { drug ->
|
Text("در حال جستجو..." , textAlign = TextAlign.Right)
|
||||||
DrugItem(
|
|
||||||
drug = drug,
|
|
||||||
onClick = { onDrugClick(drug.cod.toString()) }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DrugItem(
|
|
||||||
drug: DrugDetail, // Adjust this based on your actual data class
|
|
||||||
onClick: () -> Unit
|
|
||||||
) {
|
|
||||||
Row() {
|
|
||||||
Text(drug.nam_fa.toString())
|
|
||||||
}
|
|
||||||
// Button(
|
|
||||||
// onClick = onClick,
|
|
||||||
// modifier = Modifier
|
// modifier = Modifier
|
||||||
// .fillMaxWidth()
|
// .matchParentSize()
|
||||||
// .padding(vertical = 4.dp)
|
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
|
||||||
// ) {
|
|
||||||
// drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun EmptyStateContent() {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "برای جستجو، نام دارو را وارد کنید",
|
|
||||||
modifier = Modifier.padding(32.dp)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.approagency.drug.presentation.screens
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LabScreen(
|
||||||
|
modifier: Modifier,
|
||||||
|
viewModel: HomeViewModel = koinViewModel()
|
||||||
|
){}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package com.approagency.drug.presentation.screens
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.graphics.drawable.Icon
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Build
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.NavigationBar
|
||||||
|
import androidx.compose.material3.NavigationBarDefaults
|
||||||
|
import androidx.compose.material3.NavigationBarItem
|
||||||
|
import androidx.compose.material3.NavigationBarItemDefaults
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.ScaffoldDefaults
|
||||||
|
import androidx.compose.material3.SegmentedButtonDefaults.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
@SuppressLint("ConfigurationScreenWidthHeight")
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun RootScreen(navHostController: NavHostController, modifier: Modifier){
|
||||||
|
val pages = listOf(
|
||||||
|
"دارو" to Icons.Default.Search,
|
||||||
|
"آزمایش" to Icons.Default.Build
|
||||||
|
)
|
||||||
|
var seletedTab by remember { mutableStateOf(0) }
|
||||||
|
val context = LocalContext.current
|
||||||
|
val activity = context as Activity
|
||||||
|
|
||||||
|
Scaffold (
|
||||||
|
contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
|
||||||
|
topBar = { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl){
|
||||||
|
TopAppBar(
|
||||||
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
titleContentColor = MaterialTheme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
title = {
|
||||||
|
Text("جستجوی دارویی" , textAlign = TextAlign.Right , style = MaterialTheme.typography.titleLarge , fontWeight = FontWeight.W700 )
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}},
|
||||||
|
// modifier = modifier.fillMaxSize(),
|
||||||
|
bottomBar = {
|
||||||
|
NavigationBar(
|
||||||
|
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(110.dp).padding(bottom = 2.dp).clip(MaterialTheme.shapes.extraLarge), // 👈 کوچیکتر از حالت پیشفرض
|
||||||
|
tonalElevation = 8.dp, // subtle shadow
|
||||||
|
windowInsets = WindowInsets.navigationBars
|
||||||
|
.only(WindowInsetsSides.Bottom)
|
||||||
|
) {
|
||||||
|
pages.forEachIndexed { index, item ->
|
||||||
|
val selected = seletedTab == index
|
||||||
|
|
||||||
|
NavigationBarItem(
|
||||||
|
colors = NavigationBarItemDefaults.colors(
|
||||||
|
selectedIconColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
selectedTextColor = MaterialTheme.colorScheme.primary,
|
||||||
|
indicatorColor = MaterialTheme.colorScheme.primary,
|
||||||
|
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
),
|
||||||
|
selected = selected,
|
||||||
|
onClick = { seletedTab = index },
|
||||||
|
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = item.second,
|
||||||
|
contentDescription = item.first,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
item.first,
|
||||||
|
style = MaterialTheme.typography.labelMedium
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
alwaysShowLabel = true // 👈 فقط وقتی select شد label نشون بده
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
padding -> when (seletedTab) {
|
||||||
|
0 -> HomeScreen( navController = navHostController,Modifier.padding(padding).consumeWindowInsets(padding))
|
||||||
|
1 -> LabScreen(Modifier.padding(padding))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,15 @@ package com.approagency.drug.presentation.viewModel
|
|||||||
import android.net.http.HttpException
|
import android.net.http.HttpException
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
|
import com.approagency.drug.data.dto.DrugDetail
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import com.approagency.drug.domain.model.DrugSearchParams
|
import com.approagency.drug.domain.model.DrugSearchParams
|
||||||
|
import com.approagency.drug.domain.usecase.GetDarmanUseCase
|
||||||
|
import com.approagency.drug.domain.usecase.GetDrugDetailUseCase
|
||||||
import com.approagency.drug.domain.usecase.GetDrugSearchUseCase
|
import com.approagency.drug.domain.usecase.GetDrugSearchUseCase
|
||||||
|
import com.approagency.drug.presentation.screens.HomeScreen
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@@ -14,24 +20,53 @@ import kotlinx.coroutines.flow.update
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class HomeViewModel (
|
class HomeViewModel (
|
||||||
private val getDrugSearchUseCase: GetDrugSearchUseCase
|
private val getDrugSearchUseCase: GetDrugSearchUseCase,
|
||||||
|
private val getDrugDetailUseCase: GetDrugDetailUseCase,
|
||||||
|
private val getDarmanUseCase: GetDarmanUseCase
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
private val _uiState = MutableStateFlow(DrugSearchState())
|
private val _uiState = MutableStateFlow(CombineState())
|
||||||
val uiState : StateFlow<DrugSearchState> = _uiState
|
val uiState : StateFlow<CombineState> = _uiState
|
||||||
|
|
||||||
|
|
||||||
// Add SharedFlow for one-time events
|
// Add SharedFlow for one-time events
|
||||||
private val _event = MutableSharedFlow<HomeEvent>()
|
private val _event = MutableSharedFlow<HomeEvent>()
|
||||||
val event = _event.asSharedFlow()
|
val event = _event.asSharedFlow()
|
||||||
|
init {
|
||||||
|
getDarmani()
|
||||||
|
}
|
||||||
|
fun getDarmani(){
|
||||||
|
_uiState.update { it.copy(darmanState = it.darmanState.copy(
|
||||||
|
isLoading = true
|
||||||
|
) , showDarmanList = true ) }
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val result = getDarmanUseCase.invoke()
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
darmanState = it.darmanState.copy(
|
||||||
|
isLoading = false,
|
||||||
|
getDarmani = result
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: HttpException){
|
||||||
|
handleError(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun searchDrugs(drugSearchParams: DrugSearchParams) {
|
fun searchDrugs(drugSearchParams: DrugSearchParams) {
|
||||||
_uiState.update { it.copy(isLoading = true) }
|
_uiState.update { it.copy(drugSearchState = it.drugSearchState.copy(
|
||||||
|
isLoading = true
|
||||||
|
) , showDarmanList = false) }
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
val result = getDrugSearchUseCase.invoke(drugSearchParams)
|
val result = getDrugSearchUseCase.invoke(drugSearchParams)
|
||||||
_uiState.update { it.copy(
|
_uiState.update { it.copy(
|
||||||
|
drugSearchState = it.drugSearchState.copy(
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
drugsData = result
|
drugsData = result
|
||||||
|
)
|
||||||
|
|
||||||
) }
|
) }
|
||||||
} catch (e: HttpException) {
|
} catch (e: HttpException) {
|
||||||
handleError(e.message)
|
handleError(e.message)
|
||||||
@@ -41,26 +76,90 @@ class HomeViewModel (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun drugDetail(cod: Int , allDrugs:Result<DrugListResponse?>?) {
|
||||||
|
_uiState.update { it.copy(drugDetailState = it.drugDetailState.copy(
|
||||||
|
isLoading = true
|
||||||
|
) , showDarmanList = false) }
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val result = getDrugDetailUseCase.invoke(cod)
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
drugDetailState = it.drugDetailState.copy(
|
||||||
|
isLoading = false,
|
||||||
|
drugDetail = result,
|
||||||
|
isDetailVisible = true
|
||||||
|
),
|
||||||
|
drugSearchState = it.drugSearchState.copy(
|
||||||
|
isLoading = false,
|
||||||
|
drugsData = allDrugs
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
println(result)
|
||||||
|
} catch (e: HttpException) {
|
||||||
|
handleError(e.message)
|
||||||
|
}catch (e: Exception) {
|
||||||
|
handleError(e.localizedMessage ?: "Unknown error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun closeDetail() {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
drugDetailState = it.drugDetailState.copy(
|
||||||
|
isDetailVisible = false,
|
||||||
|
drugDetail = null // optional, depends if you want to keep last detail cached
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun handleError(message: String?) {
|
private suspend fun handleError(message: String?) {
|
||||||
_uiState.update { it.copy(isLoading = false) }
|
_uiState.update { it.copy(drugSearchState = it.drugSearchState.copy(
|
||||||
|
isLoading = false
|
||||||
|
) , drugDetailState = it.drugDetailState.copy(
|
||||||
|
isLoading = false
|
||||||
|
)) }
|
||||||
_event.emit(HomeEvent.ShowError(message ?: "Error occurred"))
|
_event.emit(HomeEvent.ShowError(message ?: "Error occurred"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear error when consumed
|
// Clear error when consumed
|
||||||
fun clearError() {
|
fun clearError() {
|
||||||
_uiState.update { it.copy(error = null) }
|
// _uiState.update { it.copy(error = null) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sealed class HomeEvent {
|
sealed class HomeEvent {
|
||||||
data class ShowError(val message: String) : HomeEvent()
|
data class ShowError(val message: String) : HomeEvent()
|
||||||
data class NavigateToDetails(val drugId: String) : HomeEvent()
|
data class ShowDetailDrug(val drugDetail:Result<DrugModels?>?): HomeEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class CombineState(
|
||||||
|
val drugSearchState: DrugSearchState = DrugSearchState() ,
|
||||||
|
val drugDetailState: DrugDetailState =DrugDetailState(),
|
||||||
|
val darmanState: GetDarmaniState = GetDarmaniState(),
|
||||||
|
val showDarmanList: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
data class DrugSearchState(
|
data class DrugSearchState(
|
||||||
val isLoading: Boolean = false,
|
val isLoading: Boolean = false,
|
||||||
val drugsData: Result<DrugListResponse?>? = null,
|
val drugsData: Result<DrugListResponse?>? = null,
|
||||||
|
val drugDetail: Result<DrugModels?>? = null,
|
||||||
|
val error:String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DrugDetailState(
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val drugDetail:Result<DrugModels?>? = null,
|
||||||
|
val isDetailVisible: Boolean = false,
|
||||||
|
val error:String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class GetDarmaniState(
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val getDarmani:Result<DarmanModel?>? = null,
|
||||||
val error:String? = null
|
val error:String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,38 +11,84 @@ import androidx.compose.material3.dynamicLightColorScheme
|
|||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import com.fabirt.podcastapp.ui.theme.Shapes
|
import com.fabirt.podcastapp.ui.theme.Shapes
|
||||||
import com.vada.caller.ui.theme.Dime
|
import com.vada.caller.ui.theme.Dime
|
||||||
import com.vada.caller.ui.theme.LocalDime
|
import com.vada.caller.ui.theme.LocalDime
|
||||||
|
object MedicalColors {
|
||||||
|
|
||||||
|
val Primary = Color(0xFF0F766E)
|
||||||
|
val PrimaryContainer = Color(0xFFD7F3EF)
|
||||||
|
|
||||||
|
val Secondary = Color(0xFF5B8E8A)
|
||||||
|
val SecondaryContainer = Color(0xFFE3F3F1)
|
||||||
|
|
||||||
|
val Tertiary = Color(0xFF8FBFB8)
|
||||||
|
|
||||||
|
val Background = Color(0xFFF8FCFB)
|
||||||
|
val Surface = Color(0xFFFFFFFF)
|
||||||
|
|
||||||
|
val DarkBackground = Color(0xFF0F1720)
|
||||||
|
val DarkSurface = Color(0xFF17232B)
|
||||||
|
|
||||||
|
val OnDark = Color(0xFFF5FAF9)
|
||||||
|
val OnLight = Color(0xFF102A2A)
|
||||||
|
|
||||||
|
val Error = Color(0xFFD64545)
|
||||||
|
|
||||||
|
val Outline = Color(0xFFD0E2DF)
|
||||||
|
}
|
||||||
private val DarkColorScheme = darkColorScheme(
|
private val DarkColorScheme = darkColorScheme(
|
||||||
primary = Purple80,
|
primary = Color(0xFF5DD4C3),
|
||||||
secondary = PurpleGrey80,
|
onPrimary = Color(0xFF003733),
|
||||||
tertiary = Pink80
|
|
||||||
|
secondary = Color(0xFF8CC9C1),
|
||||||
|
onSecondary = Color(0xFF0D2B29),
|
||||||
|
|
||||||
|
tertiary = Color(0xFFA7DCD4),
|
||||||
|
onTertiary = Color(0xFF102A2A),
|
||||||
|
|
||||||
|
background = MedicalColors.DarkBackground,
|
||||||
|
onBackground = MedicalColors.OnDark,
|
||||||
|
|
||||||
|
surface = MedicalColors.DarkSurface,
|
||||||
|
onSurface = MedicalColors.OnDark,
|
||||||
|
|
||||||
|
primaryContainer = Color(0xFF124A45),
|
||||||
|
secondaryContainer = Color(0xFF1D3D3A),
|
||||||
|
|
||||||
|
error = Color(0xFFFF8A80)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val LightColorScheme = lightColorScheme(
|
private val LightColorScheme = lightColorScheme(
|
||||||
primary = Purple40,
|
primary = MedicalColors.Primary,
|
||||||
secondary = PurpleGrey40,
|
|
||||||
tertiary = Pink40
|
|
||||||
|
|
||||||
/* Other default colors to override
|
|
||||||
background = Color(0xFFFFFBFE),
|
|
||||||
surface = Color(0xFFFFFBFE),
|
|
||||||
onPrimary = Color.White,
|
onPrimary = Color.White,
|
||||||
|
|
||||||
|
secondary = MedicalColors.Secondary,
|
||||||
onSecondary = Color.White,
|
onSecondary = Color.White,
|
||||||
onTertiary = Color.White,
|
|
||||||
onBackground = Color(0xFF1C1B1F),
|
tertiary = MedicalColors.Tertiary,
|
||||||
onSurface = Color(0xFF1C1B1F),
|
onTertiary = MedicalColors.OnLight,
|
||||||
*/
|
|
||||||
|
background = MedicalColors.Background,
|
||||||
|
onBackground = MedicalColors.OnLight,
|
||||||
|
|
||||||
|
surface = MedicalColors.Surface,
|
||||||
|
onSurface = MedicalColors.OnLight,
|
||||||
|
|
||||||
|
primaryContainer = MedicalColors.PrimaryContainer,
|
||||||
|
secondaryContainer = MedicalColors.SecondaryContainer,
|
||||||
|
|
||||||
|
outline = MedicalColors.Outline,
|
||||||
|
error = MedicalColors.Error
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DrugTheme(
|
fun DrugTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
// Dynamic color is available on Android 12+
|
// Dynamic color is available on Android 12+
|
||||||
dynamicColor: Boolean = true,
|
dynamicColor: Boolean = false,
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val colorScheme = when {
|
val colorScheme = when {
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ koinAndroid = "4.1.0"
|
|||||||
koinAndroidxCompose = "4.1.0"
|
koinAndroidxCompose = "4.1.0"
|
||||||
lifecycleViewmodelKtx = "2.9.4"
|
lifecycleViewmodelKtx = "2.9.4"
|
||||||
|
|
||||||
|
navigationCompose = "2.8.5"
|
||||||
|
material3 = "1.4.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
@@ -33,6 +36,7 @@ androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "u
|
|||||||
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
|
androidx-compose-material-icons-core = { group = "androidx.compose.material", name = "material-icons-core" }
|
||||||
|
|
||||||
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
|
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
|
||||||
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koinAndroid" }
|
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koinAndroid" }
|
||||||
@@ -45,7 +49,8 @@ gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
|
|||||||
coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
|
coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
|
||||||
location-services = { group = "com.google.android.gms", name = "play-services-location", version.ref = "location" }
|
location-services = { group = "com.google.android.gms", name = "play-services-location", version.ref = "location" }
|
||||||
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||||
|
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||||
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
#Thu Feb 26 19:18:21 IRST 2026
|
#Thu Feb 26 19:18:21 IRST 2026
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
distributionUrl=https\://maven.myket.ir/gradle/distributions/gradle-8.13-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=false
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
+6
-4
@@ -1,5 +1,6 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
|
maven { url = uri("https://maven.myket.ir") }
|
||||||
google {
|
google {
|
||||||
content {
|
content {
|
||||||
includeGroupByRegex("com\\.android.*")
|
includeGroupByRegex("com\\.android.*")
|
||||||
@@ -7,15 +8,16 @@ pluginManagement {
|
|||||||
includeGroupByRegex("androidx.*")
|
includeGroupByRegex("androidx.*")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mavenCentral()
|
// mavenCentral()
|
||||||
gradlePluginPortal()
|
// gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
//google()
|
||||||
mavenCentral()
|
//mavenCentral()
|
||||||
|
maven { url = uri("https://maven.myket.ir") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user