feat: add btn nav

This commit is contained in:
2026-05-04 13:20:04 +03:30
parent 7d3b5c32b8
commit 1fb7477b5c
26 changed files with 1240 additions and 152 deletions
+5 -1
View File
@@ -50,6 +50,7 @@ dependencies {
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
@@ -57,7 +58,7 @@ dependencies {
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
implementation(libs.androidx.compose.material.icons.core)
// ViewModel
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.lifecycle.viewmodel.ktx)
@@ -78,4 +79,7 @@ dependencies {
// Koin for DI
implementation(libs.koin.android)
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.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState
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.remember
import androidx.compose.runtime.rememberCoroutineScope
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.unit.LayoutDirection
import androidx.navigation.compose.rememberNavController
import com.approagency.drug.navigation.AppNavGraph
import com.approagency.drug.presentation.screens.HomeScreen
import com.approagency.drug.ui.theme.DrugTheme
import com.vada.caller.ui.theme.dime
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
@@ -25,18 +36,15 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge()
setContent {
DrugTheme {
Scaffold(modifier = Modifier.fillMaxSize() , topBar = {
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary,
),
title = {
Text("Small Top App Bar")
}
val navController = rememberNavController()
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
Scaffold(modifier = Modifier.fillMaxSize(),
) { innerPadding ->
AppNavGraph(
navController = navController,
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
import com.approagency.drug.data.dto.DarmanModel
import com.approagency.drug.data.dto.DrugListResponse
import com.approagency.drug.data.dto.DrugModels
import retrofit2.http.GET
@@ -21,4 +22,8 @@ interface DrugApiService {
suspend fun getDrugDetail(
@Path("cod") cod: Int
): DrugModels
@GET("drugs/goroh-darmani")
suspend fun getGorohDaroei(): DarmanModel
}
@@ -1,6 +1,8 @@
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.DrugModels
import com.approagency.drug.data.remote.DrugApiService
import com.approagency.drug.domain.model.DrugSearchParams
import com.approagency.drug.domain.repository.DrugRepository
@@ -16,4 +18,22 @@ class DrugRepositoryImpl(
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.repository.DrugRepositoryImpl
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.presentation.viewModel.HomeViewModel
import com.approagency.drug.utils.Config
@@ -34,10 +36,19 @@ val appModule= module {
//UseCase
single {
GetDrugSearchUseCase(get())
}
single{
GetDrugDetailUseCase(get())
}
single {
GetDarmanUseCase(get())
}
//view model
viewModel {
HomeViewModel(get())
HomeViewModel(get() , get() , get())
}
}
@@ -1,8 +1,12 @@
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.DrugModels
import com.approagency.drug.domain.model.DrugSearchParams
interface DrugRepository {
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(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
color = MaterialTheme.colorScheme.surface,
@@ -1,18 +1,25 @@
package com.approagency.drug.presentation.common
import android.content.res.Resources.Theme
import androidx.compose.ui.graphics.Color
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.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vada.caller.ui.theme.dime
@Composable
fun Loading() {
fun Loading(
color: Color = MaterialTheme.colorScheme.onPrimary,
modifier: Modifier = Modifier
) {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier,
strokeWidth = 1.dp
color = color,
modifier = modifier,
strokeWidth = 2.dp
)
}
@@ -1,5 +1,6 @@
package com.approagency.drug.presentation.common
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Button
@@ -8,6 +9,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
@Composable
@@ -29,8 +31,7 @@ fun PrimaryButton(
},
) {
if (isLoading == true) {
Loading(
)
Text("... در حال جستجو" , textAlign = TextAlign.Right)
} else {
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
import android.R
import android.R.attr.fontWeight
import android.content.res.Resources
import androidx.compose.foundation.background
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.Column
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.height
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.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.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.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.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -29,170 +53,240 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
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.TextUnit
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.DrugListResponse
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.Loading
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.vada.caller.ui.theme.Dime
import com.vada.caller.ui.theme.dime
import kotlinx.coroutines.flow.SharedFlow
import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen(
navController : NavController,
modifier: Modifier,
viewModel: HomeViewModel = koinViewModel()
) {
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(
modifier = modifier,
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 = {
viewModel.clearError()
viewModel.getDarmani()
},
onDrugClick = { drugId -> println(drugId) }
onDrugClick = { drugId -> viewModel.drugDetail(drugId , allDrugs = state.drugSearchState.drugsData) }
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeContent(
state: DrugSearchState,
onSearch: (String) -> Unit,
state: CombineState,
viewModelEvent: SharedFlow<HomeEvent>,
onSearch: (Any) -> Unit,
onRetry: () -> Unit,
onDrugClick: (String) -> Unit,
onDrugClick: (Int) -> Unit,
modifier: Modifier = Modifier
) {
var search by remember { mutableStateOf("") }
Column(
modifier = modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.Start
.fillMaxSize().padding(horizontal = MaterialTheme.dime.md)
.padding(top = MaterialTheme.dime.md),
horizontalAlignment = Alignment.Start,
) {
Column (
verticalArrangement = Arrangement.Center
){
Column {
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,
shape = MaterialTheme.shapes.large,
onValueChange = { value ->
search = value
},
label = { Text("جستجوی دارو") },
modifier = Modifier.fillMaxWidth(),
trailingIcon = {
IconButton(onClick = {
onValueChange = { search = it },
modifier = Modifier.weight(1f),
singleLine = true,
textStyle = MaterialTheme.typography.bodyLarge,
decorationBox = { innerTextField ->
Box {
if (search.isEmpty()) {
Text(
"جستجوی دارو",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
innerTextField()
}
}
)
IconButton(
onClick = {
if (search.isNotBlank()) {
onSearch(search)
}
}) {
},
modifier = Modifier.size(32.dp)
) {
Icon(
imageVector = Icons.Default.Search,
contentDescription = "Search"
contentDescription = "Search",
modifier = Modifier.size(18.dp)
)
}
}
)
}
}
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
PrimaryButton(
text = "جستجو",
height = 45,
isLoading = state.isLoading,
height = 40,
isLoading = state.drugSearchState.isLoading,
onClick = {
if (search.isNotBlank()) {
onSearch(search)
}
}
)
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
Box(modifier = Modifier.fillMaxSize()) {
when {
state.isLoading ->{
CustomModalDialog(
onDismissRequest = {
},
content = {
// Always draw the data if available
if (state.drugSearchState.drugsData != null && !state.showDarmanList) {
Column {
Loading()
Text("در حال جستجو...")
}
Row(
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(
drugsData = state.drugsData,
drugsData = state.drugSearchState.drugsData,
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()
}
}
}
}
}
@Composable
fun DrugListContent(
drugsData: Result<DrugListResponse?>?,
onDrugClick: (String) -> Unit
) {
val drugs = drugsData?.getOrNull()?.data ?: emptyList()
if (drugs.isEmpty()) {
Text(
text = "نتیجه‌ای یافت نشد",
modifier = Modifier.padding(16.dp)
)
} else {
// Display drug list
Column {
drugs.forEach { drug ->
DrugItem(
drug = drug,
onClick = { onDrugClick(drug.cod.toString()) }
)
// Draw the loading dialog on top if needed
if (state.drugSearchState.isLoading || state.drugDetailState.isLoading || state.darmanState.isLoading) {
CustomModalDialog(
onDismissRequest = { /* maybe disable dismiss while loading */ },
content = {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Loading(color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
Text("در حال جستجو..." , textAlign = TextAlign.Right)
}
}
}
}
@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
// .fillMaxWidth()
// .padding(vertical = 4.dp)
// ) {
// 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)
// .matchParentSize()
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
)
}
}
}
}
}
@@ -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 androidx.lifecycle.ViewModel
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.DrugModels
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.presentation.screens.HomeScreen
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -14,24 +20,53 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class HomeViewModel (
private val getDrugSearchUseCase: GetDrugSearchUseCase
private val getDrugSearchUseCase: GetDrugSearchUseCase,
private val getDrugDetailUseCase: GetDrugDetailUseCase,
private val getDarmanUseCase: GetDarmanUseCase
): ViewModel() {
private val _uiState = MutableStateFlow(DrugSearchState())
val uiState : StateFlow<DrugSearchState> = _uiState
private val _uiState = MutableStateFlow(CombineState())
val uiState : StateFlow<CombineState> = _uiState
// Add SharedFlow for one-time events
private val _event = MutableSharedFlow<HomeEvent>()
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) {
_uiState.update { it.copy(isLoading = true) }
_uiState.update { it.copy(drugSearchState = it.drugSearchState.copy(
isLoading = true
) , showDarmanList = false) }
viewModelScope.launch {
try {
val result = getDrugSearchUseCase.invoke(drugSearchParams)
_uiState.update { it.copy(
drugSearchState = it.drugSearchState.copy(
isLoading = false,
drugsData = result
)
) }
} catch (e: HttpException) {
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?) {
_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"))
}
// Clear error when consumed
fun clearError() {
_uiState.update { it.copy(error = null) }
// _uiState.update { it.copy(error = null) }
}
}
sealed class 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(
val isLoading: Boolean = false,
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
)
@@ -11,38 +11,84 @@ import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import com.fabirt.podcastapp.ui.theme.Shapes
import com.vada.caller.ui.theme.Dime
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(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
primary = Color(0xFF5DD4C3),
onPrimary = Color(0xFF003733),
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(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
primary = MedicalColors.Primary,
onPrimary = Color.White,
secondary = MedicalColors.Secondary,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
tertiary = MedicalColors.Tertiary,
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
fun DrugTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
dynamicColor: Boolean = false,
content: @Composable () -> Unit
) {
val colorScheme = when {
+6 -1
View File
@@ -18,6 +18,9 @@ koinAndroid = "4.1.0"
koinAndroidxCompose = "4.1.0"
lifecycleViewmodelKtx = "2.9.4"
navigationCompose = "2.8.5"
material3 = "1.4.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
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-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
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" }
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" }
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-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+2 -2
View File
@@ -1,8 +1,8 @@
#Thu Feb 26 19:18:21 IRST 2026
distributionBase=GRADLE_USER_HOME
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
validateDistributionUrl=true
validateDistributionUrl=false
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+6 -4
View File
@@ -1,5 +1,6 @@
pluginManagement {
repositories {
maven { url = uri("https://maven.myket.ir") }
google {
content {
includeGroupByRegex("com\\.android.*")
@@ -7,15 +8,16 @@ pluginManagement {
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
// mavenCentral()
// gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
//google()
//mavenCentral()
maven { url = uri("https://maven.myket.ir") }
}
}