feat: change package name

This commit is contained in:
2026-06-05 18:02:48 +03:30
parent 92b7ab49c4
commit cbe77fbb9e
97 changed files with 3228 additions and 3395 deletions
+3 -3
View File
@@ -6,13 +6,13 @@ plugins {
} }
android { android {
namespace = "com.approagency.drug" namespace = "com.approagency.pharmacy"
compileSdk { compileSdk {
version = release(36) version = release(36)
} }
defaultConfig { defaultConfig {
applicationId = "com.approagency.drug" applicationId = "com.approagency.pharmacy"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 36
versionCode = 1 versionCode = 1
@@ -23,7 +23,7 @@ android {
buildTypes { buildTypes {
release { release {
isMinifyEnabled = false isMinifyEnabled = true
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
+1 -1
View File
@@ -4,7 +4,7 @@
"type": "APK", "type": "APK",
"kind": "Directory" "kind": "Directory"
}, },
"applicationId": "com.approagency.drug", "applicationId": "com.approagency.pharmacy",
"variantName": "release", "variantName": "release",
"elements": [ "elements": [
{ {
@@ -1,4 +1,4 @@
package com.approagency.drug package com.approagency.pharmacy
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
Binary file not shown.
@@ -1,54 +0,0 @@
package com.approagency.drug
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
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)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DrugTheme {
AppNavGraph(
// navController = navController,
// modifier = Modifier.padding(innerPadding)
)
// Scaffold(modifier = Modifier.fillMaxSize(),
// ) { innerPadding ->
// AppNavGraph(
//// navController = navController,
//// modifier = Modifier.padding(innerPadding)
// )
// }
}
}
}
}
@@ -1,24 +0,0 @@
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.DaroYabParams
import com.approagency.drug.domain.model.DaroYabSearchResult
import com.approagency.drug.domain.model.DrugDetail
import com.approagency.drug.domain.model.DrugSearchParams
import com.approagency.drug.domain.model.DrugSearchResult
import com.approagency.drug.domain.model.PharmacyDetail
import com.approagency.drug.domain.model.PharmacyItem
interface DrugRepository {
suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse>
suspend fun drugDetail(cod:Int): Result<DrugModels>
suspend fun getGorohDaroei(): Result<DarmanModel>
suspend fun searchDrugs(params:DaroYabParams):Result<DaroYabSearchResult>
suspend fun getDrugDetailFromYab(detailUrl: String):Result<DrugDetail>
suspend fun searchPharmacies(genericDrugId: String,provId: String): List<PharmacyItem>
suspend fun getPharmacyDetail(pharmacyUrl: String): PharmacyDetail
}
@@ -1,14 +0,0 @@
package com.approagency.drug.domain.usecase
import com.approagency.drug.domain.model.DaroYabParams
import com.approagency.drug.domain.model.DaroYabSearchResult
import com.approagency.drug.domain.model.DrugDetail
import com.approagency.drug.domain.repository.DrugRepository
class DrugDetailYabUseCase (
private val repository: DrugRepository
) {
suspend operator fun invoke(detailUrl: String):Result<DrugDetail> {
return repository.getDrugDetailFromYab(detailUrl)
}
}
@@ -1,13 +0,0 @@
package com.approagency.drug.domain.usecase
import com.approagency.drug.data.dto.DrugListResponse
import com.approagency.drug.domain.model.DrugSearchParams
import com.approagency.drug.domain.repository.DrugRepository
class GetDrugSearchUseCase (
private val repository: DrugRepository
){
suspend operator fun invoke(params: DrugSearchParams): Result<DrugListResponse> {
return repository.searchDrug(params)
}
}
@@ -1,17 +0,0 @@
package com.approagency.drug.domain.usecase
import com.approagency.drug.data.repository.DrugRepositoryImpl
import com.approagency.drug.domain.model.DaroYabParams
import com.approagency.drug.domain.model.DaroYabSearchResult
import com.approagency.drug.domain.model.DrugSearchParams
import com.approagency.drug.domain.model.DrugSearchResult
import com.approagency.drug.domain.repository.DrugRepository
class SearchDrugsYabUseCase (
private val repository: DrugRepository
) {
suspend operator fun invoke(query: String, pageNumber: Int = 1): Result<DaroYabSearchResult> {
val params = DaroYabParams(query = query, pageNumber = pageNumber)
return repository.searchDrugs(params)
}
}
@@ -1,25 +1,25 @@
package com.approagency.drug package com.approagency.pharmacy
import android.app.Application import android.app.Application
import com.approagency.drug.data.local.database.LabDatabase import com.approagency.pharmacy.data.local.database.LabDatabase
import com.approagency.drug.di.appModule import com.approagency.pharmacy.di.appModule
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
class DrugApp : Application(){ class DrugApp : Application(){
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
startKoin { startKoin {
androidContext(this@DrugApp) androidContext(this@DrugApp)
modules(appModule) modules(appModule)
printLogger() printLogger()
} }
// Preload database (optional, for faster access) // Preload database (optional, for faster access)
GlobalScope.launch { GlobalScope.launch {
LabDatabase.getInstance(this@DrugApp) LabDatabase.getInstance(this@DrugApp)
} }
} }
} }
@@ -0,0 +1,33 @@
package com.approagency.pharmacy
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.ExperimentalMaterial3Api
import com.approagency.pharmacy.navigation.AppNavGraph
import com.approagency.pharmacy.ui.theme.DrugTheme
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DrugTheme {
AppNavGraph(
// navController = navController,
// modifier = Modifier.padding(innerPadding)
)
// Scaffold(modifier = Modifier.fillMaxSize(),
// ) { innerPadding ->
// AppNavGraph(
//// navController = navController,
//// modifier = Modifier.padding(innerPadding)
// )
// }
}
}
}
}
@@ -1,13 +1,13 @@
package com.approagency.drug.data.dto package com.approagency.pharmacy.data.dto
data class DarmanModel( data class DarmanModel(
val success : String, val success : String,
val message:String, val message:String,
val data: List<DarmanList> val data: List<DarmanList>
) )
data class DarmanList( data class DarmanList(
val cod : Int, val cod : Int,
val nam_fa:String, val nam_fa:String,
val nam_en:String, val nam_en:String,
) )
@@ -1,74 +1,74 @@
package com.approagency.drug.data.dto package com.approagency.pharmacy.data.dto
data class DrugModels( data class DrugModels(
val success: Boolean, val success: Boolean,
val message: String?, val message: String?,
val data: DrugDetail?, val data: DrugDetail?,
val meta: Meta? val meta: Meta?
) )
data class DrugDetail( data class DrugDetail(
val cod: Int?, val cod: Int?,
val goroh_darmani_detail_cod: Int?, val goroh_darmani_detail_cod: Int?,
val goroh_daroei_cod: Int?, val goroh_daroei_cod: Int?,
val goroh_farmakologic_cod: Int?, val goroh_farmakologic_cod: Int?,
val goroh_darmani_cod: Int?, val goroh_darmani_cod: Int?,
val nam_fa: String?, val nam_fa: String?,
val nam_en: String?, val nam_en: String?,
val mavaredmasraf: String?, val mavaredmasraf: String?,
val meghdarmasraf: String?, val meghdarmasraf: String?,
val masrafdarhamelegi: String?, val masrafdarhamelegi: String?,
val masrafdarshirdehi: String?, val masrafdarshirdehi: String?,
val manemasraf: String?, val manemasraf: String?,
val avarez: String?, val avarez: String?,
val tadakhol: String?, val tadakhol: String?,
val mekanismtasir: String?, val mekanismtasir: String?,
val nokte: String?, val nokte: String?,
val hoshdar: String?, val hoshdar: String?,
val sharayetnegahdari: String?, val sharayetnegahdari: String?,
val ashkal_daroei: String?, val ashkal_daroei: String?,
val created_at: String?, val created_at: String?,
val updated_at: String?, val updated_at: String?,
val goroh_daroei: GorohDaroei?, val goroh_daroei: GorohDaroei?,
val goroh_darmani: GorohDarmani?, val goroh_darmani: GorohDarmani?,
val goroh_darmani_detail: GorohDarmaniDetail? val goroh_darmani_detail: GorohDarmaniDetail?
) )
data class GorohDaroei( data class GorohDaroei(
val cod: Int?, val cod: Int?,
val nam: String?, val nam: String?,
val created_at: String?, val created_at: String?,
val updated_at: String? val updated_at: String?
) )
data class GorohDarmani( data class GorohDarmani(
val cod: Int?, val cod: Int?,
val nam_fa: String?, val nam_fa: String?,
val nam_en: String?, val nam_en: String?,
val giyahi_ya_shimiyaei: String?, val giyahi_ya_shimiyaei: String?,
val image: String?, val image: String?,
val created_at: String?, val created_at: String?,
val updated_at: String? val updated_at: String?
) )
data class GorohDarmaniDetail( data class GorohDarmaniDetail(
val cod: Int?, val cod: Int?,
val nam: String?, val nam: String?,
val created_at: String?, val created_at: String?,
val updated_at: String? val updated_at: String?
) )
data class Meta( data class Meta(
val current_page: Int?, val current_page: Int?,
val last_page: Int?, val last_page: Int?,
val per_page: Int?, val per_page: Int?,
val total: Int? val total: Int?
) )
// For list response compatibility // For list response compatibility
data class DrugListResponse( data class DrugListResponse(
val success: Boolean, val success: Boolean,
val message: String?, val message: String?,
val data: List<DrugDetail> = emptyList(), val data: List<DrugDetail> = emptyList(),
val meta: Meta? val meta: Meta?
) )
@@ -1,46 +1,46 @@
package com.approagency.drug.data.local.dao package com.approagency.pharmacy.data.local.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.approagency.drug.data.local.entities.TestGroupEntity import com.approagency.pharmacy.data.local.entities.TestGroupEntity
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface TestGroupDao { interface TestGroupDao {
@Query("SELECT * FROM testGroup ORDER BY Id") @Query("SELECT * FROM testGroup ORDER BY Id")
fun getAllGroups(): Flow<List<TestGroupEntity>> fun getAllGroups(): Flow<List<TestGroupEntity>>
@Query("SELECT * FROM testGroup WHERE Id = :groupId") @Query("SELECT * FROM testGroup WHERE Id = :groupId")
suspend fun getGroupById(groupId: Int): TestGroupEntity? suspend fun getGroupById(groupId: Int): TestGroupEntity?
@Query("SELECT * FROM testGroup WHERE Isparent = '1'") @Query("SELECT * FROM testGroup WHERE Isparent = '1'")
fun getParentGroups(): Flow<List<TestGroupEntity>> fun getParentGroups(): Flow<List<TestGroupEntity>>
@Query("SELECT * FROM testGroup WHERE Isparent = '0'") @Query("SELECT * FROM testGroup WHERE Isparent = '0'")
fun getChildGroups(): Flow<List<TestGroupEntity>> fun getChildGroups(): Flow<List<TestGroupEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertGroup(group: TestGroupEntity) suspend fun insertGroup(group: TestGroupEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAllGroups(groups: List<TestGroupEntity>) suspend fun insertAllGroups(groups: List<TestGroupEntity>)
@Query("DELETE FROM testGroup") @Query("DELETE FROM testGroup")
suspend fun deleteAllGroups() suspend fun deleteAllGroups()
@Query(""" @Query("""
SELECT * FROM testGroup SELECT * FROM testGroup
WHERE Fname LIKE '%' || :query || '%' WHERE Fname LIKE '%' || :query || '%'
OR Ename LIKE '%' || :query || '%' OR Ename LIKE '%' || :query || '%'
OR Detail LIKE '%' || :query || '%' OR Detail LIKE '%' || :query || '%'
ORDER BY ORDER BY
CASE CASE
WHEN Fname LIKE '%' || :query || '%' THEN 1 WHEN Fname LIKE '%' || :query || '%' THEN 1
WHEN Ename LIKE '%' || :query || '%' THEN 2 WHEN Ename LIKE '%' || :query || '%' THEN 2
ELSE 3 ELSE 3
END END
""") """)
fun searchGroups(query: String): Flow<List<TestGroupEntity>> fun searchGroups(query: String): Flow<List<TestGroupEntity>>
} }
@@ -1,46 +1,46 @@
package com.approagency.drug.data.local.dao package com.approagency.pharmacy.data.local.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.approagency.drug.data.local.entities.TestItemEntity import com.approagency.pharmacy.data.local.entities.TestItemEntity
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface TestItemDao { interface TestItemDao {
@Query("SELECT * FROM testItem ORDER BY Id") @Query("SELECT * FROM testItem ORDER BY Id")
fun getAllItems(): Flow<List<TestItemEntity>> fun getAllItems(): Flow<List<TestItemEntity>>
@Query("SELECT * FROM testItem WHERE Group_Id = :groupId ORDER BY Id") @Query("SELECT * FROM testItem WHERE Group_Id = :groupId ORDER BY Id")
fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>> fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>>
@Query("SELECT * FROM testItem WHERE Id = :itemId") @Query("SELECT * FROM testItem WHERE Id = :itemId")
suspend fun getItemById(itemId: Int): TestItemEntity? suspend fun getItemById(itemId: Int): TestItemEntity?
@Query(""" @Query("""
SELECT ti.* FROM testItem ti SELECT ti.* FROM testItem ti
LEFT JOIN testGroup tg ON ti.Group_Id = tg.Id LEFT JOIN testGroup tg ON ti.Group_Id = tg.Id
WHERE ti.Title LIKE '%' || :searchQuery || '%' WHERE ti.Title LIKE '%' || :searchQuery || '%'
OR ti.Normal_Value LIKE '%' || :searchQuery || '%' OR ti.Normal_Value LIKE '%' || :searchQuery || '%'
OR ti.Detail LIKE '%' || :searchQuery || '%' OR ti.Detail LIKE '%' || :searchQuery || '%'
OR tg.Fname LIKE '%' || :searchQuery || '%' OR tg.Fname LIKE '%' || :searchQuery || '%'
OR tg.Ename LIKE '%' || :searchQuery || '%' OR tg.Ename LIKE '%' || :searchQuery || '%'
ORDER BY ORDER BY
CASE CASE
WHEN ti.Title LIKE '%' || :searchQuery || '%' THEN 1 WHEN ti.Title LIKE '%' || :searchQuery || '%' THEN 1
WHEN tg.Fname LIKE '%' || :searchQuery || '%' THEN 2 WHEN tg.Fname LIKE '%' || :searchQuery || '%' THEN 2
ELSE 3 ELSE 3
END END
""") """)
fun searchTestItems(searchQuery: String): Flow<List<TestItemEntity>> fun searchTestItems(searchQuery: String): Flow<List<TestItemEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertItem(item: TestItemEntity) suspend fun insertItem(item: TestItemEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAllItems(items: List<TestItemEntity>) suspend fun insertAllItems(items: List<TestItemEntity>)
@Query("DELETE FROM testItem") @Query("DELETE FROM testItem")
suspend fun deleteAllItems() suspend fun deleteAllItems()
} }
@@ -1,43 +1,43 @@
package com.approagency.drug.data.local.database package com.approagency.pharmacy.data.local.database
import android.content.Context import android.content.Context
import androidx.room.Database import androidx.room.Database
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.approagency.drug.data.local.dao.TestGroupDao import com.approagency.pharmacy.data.local.dao.TestGroupDao
import com.approagency.drug.data.local.dao.TestItemDao import com.approagency.pharmacy.data.local.dao.TestItemDao
import com.approagency.drug.data.local.entities.TestGroupEntity import com.approagency.pharmacy.data.local.entities.TestGroupEntity
import com.approagency.drug.data.local.entities.TestItemEntity import com.approagency.pharmacy.data.local.entities.TestItemEntity
@Database( @Database(
entities = [ entities = [
TestGroupEntity::class, TestGroupEntity::class,
TestItemEntity::class TestItemEntity::class
], ],
version = 1, version = 1,
exportSchema = false exportSchema = false
) )
abstract class LabDatabase : RoomDatabase() { abstract class LabDatabase : RoomDatabase() {
abstract fun testGroupDao(): TestGroupDao abstract fun testGroupDao(): TestGroupDao
abstract fun testItemDao(): TestItemDao abstract fun testItemDao(): TestItemDao
companion object { companion object {
@Volatile @Volatile
private var INSTANCE: LabDatabase? = null private var INSTANCE: LabDatabase? = null
fun getInstance(context: Context): LabDatabase { fun getInstance(context: Context): LabDatabase {
return INSTANCE ?: synchronized(this) { return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder( val instance = Room.databaseBuilder(
context.applicationContext, context.applicationContext,
LabDatabase::class.java, LabDatabase::class.java,
"lab_tests.db" // This will use your existing database "lab_tests.db" // This will use your existing database
) )
.createFromAsset("lab_tests.db") // IMPORTANT: Copy from assets .createFromAsset("lab_tests.db") // IMPORTANT: Copy from assets
.fallbackToDestructiveMigration() // For development only .fallbackToDestructiveMigration() // For development only
.build() .build()
INSTANCE = instance INSTANCE = instance
instance instance
} }
} }
} }
} }
@@ -1,24 +1,24 @@
package com.approagency.drug.data.local.entities package com.approagency.pharmacy.data.local.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity(tableName = "testGroup") @Entity(tableName = "testGroup")
data class TestGroupEntity( data class TestGroupEntity(
@PrimaryKey @PrimaryKey
@ColumnInfo(name = "Id") @ColumnInfo(name = "Id")
val id: Int, val id: Int,
@ColumnInfo(name = "Ename") @ColumnInfo(name = "Ename")
val ename: String?, val ename: String?,
@ColumnInfo(name = "Fname") @ColumnInfo(name = "Fname")
val fname: String?, val fname: String?,
@ColumnInfo(name = "Detail") @ColumnInfo(name = "Detail")
val detail: String?, val detail: String?,
@ColumnInfo(name = "Isparent") @ColumnInfo(name = "Isparent")
val isParent: String? val isParent: String?
) )
@@ -1,37 +1,36 @@
package com.approagency.drug.data.local.entities package com.approagency.pharmacy.data.local.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.Index
import androidx.room.Index import androidx.room.PrimaryKey
import androidx.room.PrimaryKey
@Entity(
@Entity( tableName = "testItem",
tableName = "testItem", // foreignKeys = [
// foreignKeys = [ // ForeignKey(
// ForeignKey( // entity = TestGroupEntity::class,
// entity = TestGroupEntity::class, // parentColumns = ["Id"],
// parentColumns = ["Id"], // childColumns = ["Group_Id"],
// childColumns = ["Group_Id"], // onDelete = ForeignKey.CASCADE
// onDelete = ForeignKey.CASCADE // )
// ) // ],
// ], indices = [Index(value = ["Group_Id"])]
indices = [Index(value = ["Group_Id"])] )
) data class TestItemEntity(
data class TestItemEntity( @PrimaryKey
@PrimaryKey @ColumnInfo(name = "Id")
@ColumnInfo(name = "Id") val id: Int,
val id: Int,
@ColumnInfo(name = "Group_Id")
@ColumnInfo(name = "Group_Id") val groupId: Int,
val groupId: Int,
@ColumnInfo(name = "Title")
@ColumnInfo(name = "Title") val title: String?,
val title: String?,
@ColumnInfo(name = "Normal_Value")
@ColumnInfo(name = "Normal_Value") val normalValue: String?,
val normalValue: String?,
@ColumnInfo(name = "Detail")
@ColumnInfo(name = "Detail") val detail: String?
val detail: String?
) )
@@ -1,4 +1,4 @@
package com.approagency.drug.data.remote package com.approagency.pharmacy.data.remote
import retrofit2.http.Field import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded import retrofit2.http.FormUrlEncoded
@@ -6,7 +6,6 @@ import retrofit2.http.GET
import retrofit2.http.Headers import retrofit2.http.Headers
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Path import retrofit2.http.Path
import retrofit2.http.Query
import retrofit2.http.Url import retrofit2.http.Url
interface DarooyabApiService { interface DarooyabApiService {
@@ -1,31 +1,30 @@
package com.approagency.drug.data.remote package com.approagency.pharmacy.data.remote
import com.approagency.drug.data.dto.DarmanModel import com.approagency.pharmacy.data.dto.DarmanModel
import com.approagency.drug.data.dto.DrugListResponse import com.approagency.pharmacy.data.dto.DrugListResponse
import com.approagency.drug.data.dto.DrugModels import com.approagency.pharmacy.data.dto.DrugModels
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Headers import retrofit2.http.Path
import retrofit2.http.Path import retrofit2.http.Query
import retrofit2.http.Query
interface DrugApiService {
interface DrugApiService { @GET("drugs/search")
@GET("drugs/search") suspend fun getDrugs(
suspend fun getDrugs( @Query("q")query: String?,
@Query("q")query: String?, @Query("per_page")perPage:Int? = 20,
@Query("per_page")perPage:Int? = 20, @Query("with_relations")withRelations: Boolean? =true,
@Query("with_relations")withRelations: Boolean? =true, @Query("goroh_daroei_cod")drugGroup:Int?,
@Query("goroh_daroei_cod")drugGroup:Int?, @Query("goroh_darmani_cod")healGroup:Int?,
@Query("goroh_darmani_cod")healGroup:Int?, ): DrugListResponse
): DrugListResponse
@GET("drugs/{cod}")
@GET("drugs/{cod}") suspend fun getDrugDetail(
suspend fun getDrugDetail( @Path("cod") cod: Int
@Path("cod") cod: Int ): DrugModels
): DrugModels
@GET("drugs/goroh-darmani")
@GET("drugs/goroh-darmani") suspend fun getGorohDaroei(): DarmanModel
suspend fun getGorohDaroei(): DarmanModel
} }
@@ -1,10 +1,9 @@
package com.approagency.drug.data.remote package com.approagency.pharmacy.data.remote
import com.approagency.drug.domain.model.* import com.approagency.pharmacy.domain.model.*
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.select.Evaluator
class DrugDetailParser { class DrugDetailParser {
@@ -1,7 +1,7 @@
package com.approagency.drug.data.remote package com.approagency.pharmacy.data.remote
import com.approagency.drug.domain.model.DaroYabSearchResult import com.approagency.pharmacy.domain.model.DaroYabSearchResult
import com.approagency.drug.domain.model.DrugSearchResult import com.approagency.pharmacy.domain.model.DrugSearchResult
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@@ -1,8 +1,8 @@
// data/remote/PharmacyHtmlParser.kt // data/remote/PharmacyHtmlParser.kt
package com.approagency.drug.data.remote package com.approagency.pharmacy.data.remote
import com.approagency.drug.domain.model.PharmacyDetail import com.approagency.pharmacy.domain.model.PharmacyDetail
import com.approagency.drug.domain.model.PharmacyItem import com.approagency.pharmacy.domain.model.PharmacyItem
import org.jsoup.Jsoup import org.jsoup.Jsoup
object PharmacyHtmlParser { object PharmacyHtmlParser {
@@ -1,4 +1,4 @@
package com.approagency.drug.data.remote package com.approagency.pharmacy.data.remote
import okhttp3.ResponseBody import okhttp3.ResponseBody
import retrofit2.Converter import retrofit2.Converter
@@ -1,125 +1,124 @@
package com.approagency.drug.data.repository package com.approagency.pharmacy.data.repository
import com.approagency.drug.data.dto.DarmanModel import com.approagency.pharmacy.data.dto.DarmanModel
import com.approagency.drug.data.dto.DrugListResponse import com.approagency.pharmacy.data.dto.DrugListResponse
import com.approagency.drug.data.dto.DrugModels import com.approagency.pharmacy.data.dto.DrugModels
import com.approagency.drug.data.remote.DarooyabApiService import com.approagency.pharmacy.data.remote.DarooyabApiService
import com.approagency.drug.data.remote.DrugApiService import com.approagency.pharmacy.data.remote.DrugApiService
import com.approagency.drug.data.remote.DrugDetailParser import com.approagency.pharmacy.data.remote.DrugDetailParser
import com.approagency.drug.data.remote.DrugHtmlParser import com.approagency.pharmacy.data.remote.DrugHtmlParser
import com.approagency.drug.data.remote.PharmacyHtmlParser import com.approagency.pharmacy.data.remote.PharmacyHtmlParser
import com.approagency.drug.domain.model.DaroYabParams import com.approagency.pharmacy.domain.model.DaroYabParams
import com.approagency.drug.domain.model.DaroYabSearchResult import com.approagency.pharmacy.domain.model.DaroYabSearchResult
import com.approagency.drug.domain.model.DrugDetail import com.approagency.pharmacy.domain.model.DrugDetail
import com.approagency.drug.domain.model.DrugSearchParams import com.approagency.pharmacy.domain.model.DrugSearchParams
import com.approagency.drug.domain.model.DrugSearchResult import com.approagency.pharmacy.domain.model.PharmacyDetail
import com.approagency.drug.domain.model.PharmacyDetail import com.approagency.pharmacy.domain.model.PharmacyItem
import com.approagency.drug.domain.model.PharmacyItem import com.approagency.pharmacy.domain.repository.DrugRepository
import com.approagency.drug.domain.repository.DrugRepository import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext
import kotlinx.coroutines.withContext import java.io.IOException
import java.io.IOException
class DrugRepositoryImpl(
class DrugRepositoryImpl( private val apiService: DrugApiService,
private val apiService: DrugApiService, private val darooyabApiService: DarooyabApiService,
private val darooyabApiService: DarooyabApiService, private val parser: DrugHtmlParser,
private val parser: DrugHtmlParser, private val detailParser: DrugDetailParser,
private val detailParser: DrugDetailParser, ): DrugRepository {
): DrugRepository { override suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse> {
override suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse> { return try {
return try { val response = apiService.getDrugs(query = params.query , perPage = params.perPage , withRelations = params.withRelations , healGroup = params.healGroup , drugGroup = params.drugGroup )
val response = apiService.getDrugs(query = params.query , perPage = params.perPage , withRelations = params.withRelations , healGroup = params.healGroup , drugGroup = params.drugGroup ) return Result.success(response)
return Result.success(response) }catch (e: Exception){
}catch (e: Exception){ Result.failure(e)
Result.failure(e) }
} }
}
override suspend fun drugDetail(cod: Int): Result<DrugModels> {
override suspend fun drugDetail(cod: Int): Result<DrugModels> { return try {
return try { val response= apiService.getDrugDetail(cod = cod)
val response= apiService.getDrugDetail(cod = cod) println(response.message)
println(response.message) return Result.success(response)
return Result.success(response) }catch (e: Exception){
}catch (e: Exception){ println(e.message)
println(e.message) Result.failure(e)
Result.failure(e) }
} }
}
override suspend fun getGorohDaroei(): Result<DarmanModel> {
override suspend fun getGorohDaroei(): Result<DarmanModel> { return try {
return try { val response = apiService.getGorohDaroei()
val response = apiService.getGorohDaroei() println(response)
println(response) return Result.success(response)
return Result.success(response) }catch (e: Exception){
}catch (e: Exception){ Result.failure(e)
Result.failure(e) }
} }
}
override suspend fun searchDrugs(params: DaroYabParams): Result<DaroYabSearchResult> {
override suspend fun searchDrugs(params: DaroYabParams): Result<DaroYabSearchResult> { return withContext(Dispatchers.IO) {
return withContext(Dispatchers.IO) { try {
try { val htmlResponse = darooyabApiService.searchDrugs(
val htmlResponse = darooyabApiService.searchDrugs( searchText = params.query ?: "",
searchText = params.query ?: "", pageNumber = params.pageNumber
pageNumber = params.pageNumber )
)
println("HTML Response length: ${htmlResponse.length}")
println("HTML Response length: ${htmlResponse.length}")
val searchResult = parser.parseSearchResultsWithPagination(htmlResponse)
val searchResult = parser.parseSearchResultsWithPagination(htmlResponse)
if (searchResult.drugs.isNotEmpty()) {
if (searchResult.drugs.isNotEmpty()) { Result.success(searchResult)
Result.success(searchResult) } else {
} else { Result.failure(Exception("No drugs found for query: '${params.query}'"))
Result.failure(Exception("No drugs found for query: '${params.query}'")) }
} } catch (e: IOException) {
} catch (e: IOException) { Result.failure(Exception("Network error: ${e.message}", e))
Result.failure(Exception("Network error: ${e.message}", e)) } catch (e: Exception) {
} catch (e: Exception) { Result.failure(Exception("An error occurred: ${e.message}", e))
Result.failure(Exception("An error occurred: ${e.message}", e)) }
} }
} }
}
override suspend fun getDrugDetailFromYab(detailUrl: String): Result<DrugDetail> {
override suspend fun getDrugDetailFromYab(detailUrl: String): Result<DrugDetail> { return withContext(Dispatchers.IO) {
return withContext(Dispatchers.IO) { try {
try { val html = darooyabApiService.getDrugDetail(detailUrl)
val html = darooyabApiService.getDrugDetail(detailUrl) val detail = detailParser.parseDrugDetail(html)
val detail = detailParser.parseDrugDetail(html) Result.success(detail)
Result.success(detail) } catch (e: Exception) {
} catch (e: Exception) { Result.failure(e)
Result.failure(e) }
} }
} }
}
override suspend fun searchPharmacies(
override suspend fun searchPharmacies( genericDrugId: String,
genericDrugId: String, provId: String
provId: String ): List<PharmacyItem> {
): List<PharmacyItem> { return withContext(Dispatchers.IO) {
return withContext(Dispatchers.IO) { try {
try { val html = darooyabApiService.getPharmacies(
val html = darooyabApiService.getPharmacies( brandIrc = "0",
brandIrc = "0", genericDrugId = genericDrugId,
genericDrugId = genericDrugId, provId = provId,
provId = provId, cityId = "0"
cityId = "0" )
) PharmacyHtmlParser.parse(html)
PharmacyHtmlParser.parse(html) } catch (e: Exception) {
} catch (e: Exception) { emptyList()
emptyList() }
} }
} }
}
override suspend fun getPharmacyDetail(pharmacyUrl: String): PharmacyDetail {
override suspend fun getPharmacyDetail(pharmacyUrl: String): PharmacyDetail { return withContext(Dispatchers.IO) {
return withContext(Dispatchers.IO) { try {
try { val html = darooyabApiService.getPharmacyDetail(pharmacyUrl)
val html = darooyabApiService.getPharmacyDetail(pharmacyUrl) PharmacyHtmlParser.parsePharmacyDetail(html)
PharmacyHtmlParser.parsePharmacyDetail(html) } catch (e: Exception) {
} catch (e: Exception) { PharmacyDetail("", "", "")
PharmacyDetail("", "", "") }
} }
} }
}
} }
@@ -1,35 +1,35 @@
package com.approagency.drug.data.repository package com.approagency.pharmacy.data.repository
import com.approagency.drug.data.local.dao.TestGroupDao import com.approagency.pharmacy.data.local.dao.TestGroupDao
import com.approagency.drug.data.local.dao.TestItemDao import com.approagency.pharmacy.data.local.dao.TestItemDao
import com.approagency.drug.data.local.entities.TestGroupEntity import com.approagency.pharmacy.data.local.entities.TestGroupEntity
import com.approagency.drug.data.local.entities.TestItemEntity import com.approagency.pharmacy.data.local.entities.TestItemEntity
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
class LabRepositoryImpl ( class LabRepositoryImpl (
private val testGroupDao: TestGroupDao, private val testGroupDao: TestGroupDao,
private val testItemDao: TestItemDao private val testItemDao: TestItemDao
) { ) {
// Test Group Operations // Test Group Operations
fun getAllGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getAllGroups() fun getAllGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getAllGroups()
fun getParentGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getParentGroups() fun getParentGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getParentGroups()
fun getChildGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getChildGroups() fun getChildGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getChildGroups()
suspend fun getGroupById(groupId: Int): TestGroupEntity? = testGroupDao.getGroupById(groupId) suspend fun getGroupById(groupId: Int): TestGroupEntity? = testGroupDao.getGroupById(groupId)
// Test Item Operations // Test Item Operations
fun getAllItems(): Flow<List<TestItemEntity>> = testItemDao.getAllItems() fun getAllItems(): Flow<List<TestItemEntity>> = testItemDao.getAllItems()
fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>> = testItemDao.getItemsByGroupId(groupId) fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>> = testItemDao.getItemsByGroupId(groupId)
suspend fun getItemById(itemId: Int): TestItemEntity? = testItemDao.getItemById(itemId) suspend fun getItemById(itemId: Int): TestItemEntity? = testItemDao.getItemById(itemId)
fun searchTestItems(query: String): Flow<List<TestItemEntity>> = testItemDao.searchTestItems(query) fun searchTestItems(query: String): Flow<List<TestItemEntity>> = testItemDao.searchTestItems(query)
fun searchGroupsAndItems(searchQuery: String): Flow<Pair<List<TestGroupEntity>, List<TestItemEntity>>> { fun searchGroupsAndItems(searchQuery: String): Flow<Pair<List<TestGroupEntity>, List<TestItemEntity>>> {
return combine( return combine(
testGroupDao.searchGroups(searchQuery), testGroupDao.searchGroups(searchQuery),
testItemDao.searchTestItems(searchQuery) testItemDao.searchTestItems(searchQuery)
) { groups, items -> ) { groups, items ->
Pair(groups, items) Pair(groups, items)
} }
} }
} }
@@ -1,163 +1,162 @@
package com.approagency.drug.di package com.approagency.pharmacy.di
import android.app.Application import com.approagency.pharmacy.data.local.database.LabDatabase
import com.approagency.drug.data.local.database.LabDatabase import com.approagency.pharmacy.data.remote.DarooyabApiService
import com.approagency.drug.data.remote.DarooyabApiService import com.approagency.pharmacy.data.remote.DrugApiService
import com.approagency.drug.data.remote.DrugApiService import com.approagency.pharmacy.data.remote.DrugDetailParser
import com.approagency.drug.data.remote.DrugDetailParser import com.approagency.pharmacy.data.remote.DrugHtmlParser
import com.approagency.drug.data.remote.DrugHtmlParser import com.approagency.pharmacy.data.repository.DrugRepositoryImpl
import com.approagency.drug.data.repository.DrugRepositoryImpl import com.approagency.pharmacy.data.repository.LabRepositoryImpl
import com.approagency.drug.data.repository.LabRepositoryImpl import com.approagency.pharmacy.domain.repository.DrugRepository
import com.approagency.drug.domain.repository.DrugRepository import com.approagency.pharmacy.domain.usecase.DrugDetailYabUseCase
import com.approagency.drug.domain.usecase.DrugDetailYabUseCase import com.approagency.pharmacy.domain.usecase.GetDarmanUseCase
import com.approagency.drug.domain.usecase.GetDarmanUseCase import com.approagency.pharmacy.domain.usecase.GetDrugDetailUseCase
import com.approagency.drug.domain.usecase.GetDrugDetailUseCase import com.approagency.pharmacy.domain.usecase.GetDrugSearchUseCase
import com.approagency.drug.domain.usecase.GetDrugSearchUseCase import com.approagency.pharmacy.domain.usecase.GetPharmaciesUseCase
import com.approagency.drug.domain.usecase.GetPharmaciesUseCase import com.approagency.pharmacy.domain.usecase.GetPharmacyDetailUseCase
import com.approagency.drug.domain.usecase.GetPharmacyDetailUseCase import com.approagency.pharmacy.domain.usecase.GetTestGroupUseCase
import com.approagency.drug.domain.usecase.GetTestGroupUseCase import com.approagency.pharmacy.domain.usecase.GetTestItemByGroupId
import com.approagency.drug.domain.usecase.GetTestItemByGroupId import com.approagency.pharmacy.domain.usecase.SearchDrugsYabUseCase
import com.approagency.drug.domain.usecase.SearchDrugsYabUseCase import com.approagency.pharmacy.domain.usecase.SearchTestsUseCase
import com.approagency.drug.domain.usecase.SearchTestsUseCase import com.approagency.pharmacy.presentation.viewModel.DrugDetailViewModel
import com.approagency.drug.presentation.viewModel.DrugDetailViewModel import com.approagency.pharmacy.presentation.viewModel.HomeViewModel
import com.approagency.drug.presentation.viewModel.HomeViewModel import com.approagency.pharmacy.presentation.viewModel.LabViewModel
import com.approagency.drug.presentation.viewModel.LabViewModel import com.approagency.pharmacy.presentation.viewModel.PharmacyViewModel
import com.approagency.drug.presentation.viewModel.PharmacyViewModel import com.approagency.pharmacy.utils.Config
import com.approagency.drug.utils.Config import com.approgency.drug.presentation.viewModel.SearchViewModel
import com.approgency.drug.presentation.viewModel.SearchViewModel import okhttp3.OkHttpClient
import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.logging.HttpLoggingInterceptor import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidContext import org.koin.core.module.dsl.viewModel
import org.koin.core.module.dsl.viewModel import org.koin.core.qualifier.qualifier
import org.koin.core.qualifier.qualifier import org.koin.dsl.module
import org.koin.dsl.module import retrofit2.Retrofit
import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.scalars.ScalarsConverterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory import java.net.CookieManager
import java.net.CookieManager import java.net.CookiePolicy
import java.net.CookiePolicy import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit val jsonRetrofitQualifier = qualifier("jsonRetrofit")
val jsonRetrofitQualifier = qualifier("jsonRetrofit") val scalarRetrofitQualifier = qualifier("scalarRetrofit")
val scalarRetrofitQualifier = qualifier("scalarRetrofit") val appModule= module {
val appModule= module {
single { LabDatabase.getInstance(androidContext()) }
single { LabDatabase.getInstance(androidContext()) }
single { get<LabDatabase>().testGroupDao() }
single { get<LabDatabase>().testGroupDao() } single { get<LabDatabase>().testItemDao() }
single { get<LabDatabase>().testItemDao() }
// Shared OkHttpClient for both APIs
// Shared OkHttpClient for both APIs single {
single { val cookieManager = CookieManager()
val cookieManager = CookieManager() cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL)
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL)
OkHttpClient.Builder()
OkHttpClient.Builder() .addInterceptor(HttpLoggingInterceptor().apply {
.addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY // Change to BODY for debugging
level = HttpLoggingInterceptor.Level.BODY // Change to BODY for debugging })
}) // Add cookie support
// Add cookie support .connectTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .followRedirects(true)
.followRedirects(true) .build()
.build() }
}
single<Retrofit>(jsonRetrofitQualifier) { // Add qualifier
single<Retrofit>(jsonRetrofitQualifier) { // Add qualifier Retrofit.Builder()
Retrofit.Builder() .baseUrl(Config.BASE_URL)
.baseUrl(Config.BASE_URL) .client(get<OkHttpClient>())
.client(get<OkHttpClient>()) .addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create()) .build()
.build() }
}
single<DrugApiService> {
single<DrugApiService> { val retrofit: Retrofit = get(jsonRetrofitQualifier) // Get qualified instance
val retrofit: Retrofit = get(jsonRetrofitQualifier) // Get qualified instance retrofit.create(DrugApiService::class.java)
retrofit.create(DrugApiService::class.java) }
}
// ========== Retrofit for DAROOYAB Website ==========
// ========== Retrofit for DAROOYAB Website ========== single<Retrofit>(scalarRetrofitQualifier) { // Add qualifier
single<Retrofit>(scalarRetrofitQualifier) { // Add qualifier Retrofit.Builder()
Retrofit.Builder() .baseUrl(Config.Darro_Url)
.baseUrl(Config.Darro_Url) .client(get<OkHttpClient>())
.client(get<OkHttpClient>()) .addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create()) .build()
.build() }
}
single<DarooyabApiService> {
single<DarooyabApiService> { val retrofit: Retrofit = get(scalarRetrofitQualifier) // Get qualified instance
val retrofit: Retrofit = get(scalarRetrofitQualifier) // Get qualified instance retrofit.create(DarooyabApiService::class.java)
retrofit.create(DarooyabApiService::class.java) }
}
factory { DrugHtmlParser() }
factory { DrugHtmlParser() } factory { DrugDetailParser() }
factory { DrugDetailParser() } //repo
//repo single<DrugRepository> {
single<DrugRepository> { DrugRepositoryImpl(get<DrugApiService>() , get<DarooyabApiService>() , get() , get())
DrugRepositoryImpl(get<DrugApiService>() , get<DarooyabApiService>() , get() , get()) }
}
//UseCase
//UseCase single {
single { GetDrugSearchUseCase(get())
GetDrugSearchUseCase(get())
}
}
single {
single { SearchDrugsYabUseCase(get())
SearchDrugsYabUseCase(get()) }
}
single{
single{ GetDrugDetailUseCase(get())
GetDrugDetailUseCase(get()) }
}
single {
single { GetDarmanUseCase(get())
GetDarmanUseCase(get()) }
}
single {
single { GetTestGroupUseCase(get())
GetTestGroupUseCase(get()) }
}
single {
single { GetPharmaciesUseCase(get())
GetPharmaciesUseCase(get()) }
}
single {
single { GetTestItemByGroupId(get())
GetTestItemByGroupId(get()) }
}
single { LabRepositoryImpl(get(), get()) }
single { LabRepositoryImpl(get(), get()) }
single {
single { DrugDetailYabUseCase(get())
DrugDetailYabUseCase(get()) }
}
single {
single { GetPharmacyDetailUseCase(get())
GetPharmacyDetailUseCase(get()) }
}
viewModel {
viewModel { DrugDetailViewModel(get())
DrugDetailViewModel(get()) }
}
single { SearchTestsUseCase(get()) }
single { SearchTestsUseCase(get()) } //view model
//view model viewModel {
viewModel { HomeViewModel(get() , get() , get())
HomeViewModel(get() , get() , get()) }
}
viewModel {
viewModel { SearchViewModel(get())
SearchViewModel(get()) }
} viewModel {
viewModel { PharmacyViewModel(get() , get ())
PharmacyViewModel(get() , get ()) }
}
viewModel {
viewModel { LabViewModel(get() , get() , get())
LabViewModel(get() , get() , get()) }
}
} }
@@ -1,4 +1,4 @@
package com.approagency.drug.domain.model package com.approagency.pharmacy.domain.model
data class DaroYabParams( data class DaroYabParams(
val query: String? = null, val query: String? = null,
@@ -1,4 +1,4 @@
package com.approagency.drug.domain.model package com.approagency.pharmacy.domain.model
data class DaroYabSearchResult( data class DaroYabSearchResult(
val drugs: List<DrugSearchResult>, val drugs: List<DrugSearchResult>,
@@ -1,4 +1,4 @@
package com.approagency.drug.domain.model package com.approagency.pharmacy.domain.model
data class DrugDetail( data class DrugDetail(
val genericId: String, val genericId: String,
@@ -1,10 +1,10 @@
package com.approagency.drug.domain.model package com.approagency.pharmacy.domain.model
data class DrugSearchParams( data class DrugSearchParams(
val query: String? = null, val query: String? = null,
val perPage: Int = 20, val perPage: Int = 20,
val withRelations: Boolean = true, val withRelations: Boolean = true,
val drugGroup: Int? = null, val drugGroup: Int? = null,
val healGroup: Int? = null, val healGroup: Int? = null,
) )
@@ -1,4 +1,4 @@
package com.approagency.drug.domain.model package com.approagency.pharmacy.domain.model
data class DrugSearchResult( data class DrugSearchResult(
val genericId: String, // e.g., "2556" from "/G-2556/Casanthranol" val genericId: String, // e.g., "2556" from "/G-2556/Casanthranol"
@@ -1,4 +1,4 @@
package com.approagency.drug.domain.model package com.approagency.pharmacy.domain.model
data class PharmacySearchRequest( data class PharmacySearchRequest(
val brandIrc: String = "0", // برای صفحات برند val brandIrc: String = "0", // برای صفحات برند
@@ -1,22 +1,22 @@
package com.approagency.drug.domain.model package com.approagency.pharmacy.domain.model
import com.approagency.drug.data.local.entities.TestGroupEntity import com.approagency.pharmacy.data.local.entities.TestGroupEntity
data class TestGroup( data class TestGroup(
val id: Int, val id: Int,
val ename: String?, val ename: String?,
val fname: String?, val fname: String?,
val detail: String?, val detail: String?,
val isParent: String?, val isParent: String?,
) )
fun TestGroupEntity.toTestGroup(): TestGroup { fun TestGroupEntity.toTestGroup(): TestGroup {
return TestGroup( return TestGroup(
id = this.id, id = this.id,
ename = this.ename, ename = this.ename,
fname = this.fname, fname = this.fname,
detail = this.detail, detail = this.detail,
isParent = this.isParent isParent = this.isParent
) )
} }
@@ -1,20 +1,20 @@
package com.approagency.drug.domain.model package com.approagency.pharmacy.domain.model
import com.approagency.drug.data.local.entities.TestItemEntity import com.approagency.pharmacy.data.local.entities.TestItemEntity
data class TestItem( data class TestItem(
val id: Int, val id: Int,
val groupId: Int, val groupId: Int,
val title: String?, val title: String?,
val normalValue: String?, val normalValue: String?,
val detail: String? val detail: String?
) )
fun TestItemEntity.toTestItem(): TestItem { fun TestItemEntity.toTestItem(): TestItem {
return TestItem( return TestItem(
id = this.id, id = this.id,
groupId = this.groupId, groupId = this.groupId,
title = this.title, title = this.title,
normalValue = this.normalValue, normalValue = this.normalValue,
detail = this.detail detail = this.detail
) )
} }
@@ -0,0 +1,23 @@
package com.approagency.pharmacy.domain.repository
import com.approagency.pharmacy.data.dto.DarmanModel
import com.approagency.pharmacy.data.dto.DrugListResponse
import com.approagency.pharmacy.data.dto.DrugModels
import com.approagency.pharmacy.domain.model.DaroYabParams
import com.approagency.pharmacy.domain.model.DaroYabSearchResult
import com.approagency.pharmacy.domain.model.DrugDetail
import com.approagency.pharmacy.domain.model.DrugSearchParams
import com.approagency.pharmacy.domain.model.PharmacyDetail
import com.approagency.pharmacy.domain.model.PharmacyItem
interface DrugRepository {
suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse>
suspend fun drugDetail(cod:Int): Result<DrugModels>
suspend fun getGorohDaroei(): Result<DarmanModel>
suspend fun searchDrugs(params:DaroYabParams):Result<DaroYabSearchResult>
suspend fun getDrugDetailFromYab(detailUrl: String):Result<DrugDetail>
suspend fun searchPharmacies(genericDrugId: String,provId: String): List<PharmacyItem>
suspend fun getPharmacyDetail(pharmacyUrl: String): PharmacyDetail
}
@@ -0,0 +1,12 @@
package com.approagency.pharmacy.domain.usecase
import com.approagency.pharmacy.domain.model.DrugDetail
import com.approagency.pharmacy.domain.repository.DrugRepository
class DrugDetailYabUseCase (
private val repository: DrugRepository
) {
suspend operator fun invoke(detailUrl: String):Result<DrugDetail> {
return repository.getDrugDetailFromYab(detailUrl)
}
}
@@ -1,12 +1,12 @@
package com.approagency.drug.domain.usecase package com.approagency.pharmacy.domain.usecase
import com.approagency.drug.data.dto.DarmanModel import com.approagency.pharmacy.data.dto.DarmanModel
import com.approagency.drug.domain.repository.DrugRepository import com.approagency.pharmacy.domain.repository.DrugRepository
class GetDarmanUseCase ( class GetDarmanUseCase (
private val repository: DrugRepository private val repository: DrugRepository
){ ){
suspend operator fun invoke (): Result<DarmanModel>{ suspend operator fun invoke (): Result<DarmanModel>{
return repository.getGorohDaroei() return repository.getGorohDaroei()
} }
} }
@@ -1,12 +1,12 @@
package com.approagency.drug.domain.usecase package com.approagency.pharmacy.domain.usecase
import com.approagency.drug.data.dto.DrugModels import com.approagency.pharmacy.data.dto.DrugModels
import com.approagency.drug.domain.repository.DrugRepository import com.approagency.pharmacy.domain.repository.DrugRepository
class GetDrugDetailUseCase class GetDrugDetailUseCase
( (
private val repository: DrugRepository){ private val repository: DrugRepository){
suspend operator fun invoke(cod:Int): Result<DrugModels>{ suspend operator fun invoke(cod:Int): Result<DrugModels>{
return repository.drugDetail(cod) return repository.drugDetail(cod)
} }
} }
@@ -0,0 +1,13 @@
package com.approagency.pharmacy.domain.usecase
import com.approagency.pharmacy.data.dto.DrugListResponse
import com.approagency.pharmacy.domain.model.DrugSearchParams
import com.approagency.pharmacy.domain.repository.DrugRepository
class GetDrugSearchUseCase (
private val repository: DrugRepository
){
suspend operator fun invoke(params: DrugSearchParams): Result<DrugListResponse> {
return repository.searchDrug(params)
}
}
@@ -1,7 +1,7 @@
package com.approagency.drug.domain.usecase package com.approagency.pharmacy.domain.usecase
import com.approagency.drug.domain.model.PharmacyItem import com.approagency.pharmacy.domain.model.PharmacyItem
import com.approagency.drug.domain.repository.DrugRepository import com.approagency.pharmacy.domain.repository.DrugRepository
class GetPharmaciesUseCase( class GetPharmaciesUseCase(
private val repository: DrugRepository private val repository: DrugRepository
@@ -1,7 +1,7 @@
package com.approagency.drug.domain.usecase package com.approagency.pharmacy.domain.usecase
import com.approagency.drug.domain.model.PharmacyDetail import com.approagency.pharmacy.domain.model.PharmacyDetail
import com.approagency.drug.domain.repository.DrugRepository import com.approagency.pharmacy.domain.repository.DrugRepository
class GetPharmacyDetailUseCase( class GetPharmacyDetailUseCase(
private val repository: DrugRepository private val repository: DrugRepository
@@ -1,24 +1,24 @@
package com.approagency.drug.domain.usecase package com.approagency.pharmacy.domain.usecase
import com.approagency.drug.data.repository.LabRepositoryImpl import com.approagency.pharmacy.data.repository.LabRepositoryImpl
import com.approagency.drug.domain.model.TestGroup import com.approagency.pharmacy.domain.model.TestGroup
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
class GetTestGroupUseCase ( class GetTestGroupUseCase (
private val repository: LabRepositoryImpl private val repository: LabRepositoryImpl
) { ) {
operator fun invoke(): Flow<List<TestGroup>> { operator fun invoke(): Flow<List<TestGroup>> {
return repository.getAllGroups().map { entities -> return repository.getAllGroups().map { entities ->
entities.map { entity -> entities.map { entity ->
TestGroup( TestGroup(
id = entity.id, id = entity.id,
ename = entity.ename, ename = entity.ename,
fname = entity.fname, fname = entity.fname,
detail = entity.detail, detail = entity.detail,
isParent = entity.isParent isParent = entity.isParent
) )
} }
} }
} }
} }
@@ -1,26 +1,25 @@
package com.approagency.drug.domain.usecase package com.approagency.pharmacy.domain.usecase
import com.approagency.drug.data.local.entities.TestItemEntity import com.approagency.pharmacy.data.repository.LabRepositoryImpl
import com.approagency.drug.data.repository.LabRepositoryImpl import com.approagency.pharmacy.domain.model.TestItem
import com.approagency.drug.domain.model.TestItem import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.map
class GetTestItemByGroupId (
class GetTestItemByGroupId ( private val repository: LabRepositoryImpl
private val repository: LabRepositoryImpl ) {
) { operator fun invoke(id:Int): Flow<List<TestItem>> {
operator fun invoke(id:Int): Flow<List<TestItem>> { return repository.getItemsByGroupId(groupId = id).map {
return repository.getItemsByGroupId(groupId = id).map { entities -> entities.map {
entities -> entities.map { entity ->
entity -> TestItem(
TestItem( id = entity.id,
id = entity.id, groupId = entity.groupId,
groupId = entity.groupId, title = entity.title,
title = entity.title, normalValue = entity.normalValue,
normalValue = entity.normalValue, detail = entity.detail,
detail = entity.detail, )
) }
} }
} }
}
} }
@@ -0,0 +1,14 @@
package com.approagency.pharmacy.domain.usecase
import com.approagency.pharmacy.domain.model.DaroYabParams
import com.approagency.pharmacy.domain.model.DaroYabSearchResult
import com.approagency.pharmacy.domain.repository.DrugRepository
class SearchDrugsYabUseCase (
private val repository: DrugRepository
) {
suspend operator fun invoke(query: String, pageNumber: Int = 1): Result<DaroYabSearchResult> {
val params = DaroYabParams(query = query, pageNumber = pageNumber)
return repository.searchDrugs(params)
}
}
@@ -1,23 +1,23 @@
package com.approagency.drug.domain.usecase package com.approagency.pharmacy.domain.usecase
import com.approagency.drug.data.repository.LabRepositoryImpl import com.approagency.pharmacy.data.repository.LabRepositoryImpl
import com.approagency.drug.domain.model.TestGroup import com.approagency.pharmacy.domain.model.TestGroup
import com.approagency.drug.domain.model.TestItem import com.approagency.pharmacy.domain.model.TestItem
import com.approagency.drug.domain.model.toTestGroup import com.approagency.pharmacy.domain.model.toTestGroup
import com.approagency.drug.domain.model.toTestItem import com.approagency.pharmacy.domain.model.toTestItem
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
class SearchTestsUseCase( class SearchTestsUseCase(
private val repository: LabRepositoryImpl private val repository: LabRepositoryImpl
) { ) {
operator fun invoke(query: String): Flow<Pair<List<TestGroup>, List<TestItem>>> { operator fun invoke(query: String): Flow<Pair<List<TestGroup>, List<TestItem>>> {
return repository.searchGroupsAndItems(query).map { (groups, items) -> return repository.searchGroupsAndItems(query).map { (groups, items) ->
Pair( Pair(
groups.map { it.toTestGroup() }, groups.map { it.toTestGroup() },
items.map { it.toTestItem() } items.map { it.toTestItem() }
) )
} }
} }
} }
@@ -1,4 +1,4 @@
package com.approagency.drug.navigation package com.approagency.pharmacy.navigation
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -8,10 +8,10 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument import androidx.navigation.navArgument
import com.approagency.drug.presentation.screens.DrugDetailScreen import com.approagency.pharmacy.presentation.screens.DrugDetailScreen
import com.approagency.drug.presentation.screens.HomeScreen import com.approagency.pharmacy.presentation.screens.HomeScreen
import com.approagency.drug.presentation.screens.LabScreen import com.approagency.pharmacy.presentation.screens.LabScreen
import com.approagency.drug.presentation.screens.SearchScreen import com.approagency.pharmacy.presentation.screens.SearchScreen
@Composable @Composable
fun AppNavGraph() { fun AppNavGraph() {
@@ -1,31 +1,22 @@
package com.approagency.drug.navigation package com.approagency.pharmacy.navigation
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
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.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.LocalPharmacy import androidx.compose.material.icons.filled.LocalPharmacy
import androidx.compose.material.icons.filled.Science import androidx.compose.material.icons.filled.Science
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -35,8 +26,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
@@ -1,14 +1,12 @@
package com.approagency.drug.navigation package com.approagency.pharmacy.navigation
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.currentBackStackEntryAsState
@Composable @Composable
fun MainContainer( fun MainContainer(
navController: NavHostController, navController: NavHostController,
@@ -1,4 +1,4 @@
package com.approagency.drug.navigation package com.approagency.pharmacy.navigation
import android.net.Uri import android.net.Uri
@@ -1,39 +1,37 @@
package com.approagency.drug.presentation.common package com.approagency.pharmacy.presentation.common
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import com.vada.caller.ui.theme.dime
import com.vada.caller.ui.theme.dime import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class)
@Composable
@OptIn(ExperimentalMaterial3Api::class) fun CustomModalBottomSheet(
@Composable modifier: Modifier = Modifier,
fun CustomModalBottomSheet( sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
modifier: Modifier = Modifier, scope: CoroutineScope = rememberCoroutineScope(),
sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), onDismiss: (() -> Unit)? = null,
scope: CoroutineScope = rememberCoroutineScope(), content: @Composable () -> Unit
onDismiss: (() -> Unit)? = null, ) {
content: @Composable () -> Unit ModalBottomSheet(
) { onDismissRequest = {
ModalBottomSheet( onDismiss?.invoke()
onDismissRequest = { },
onDismiss?.invoke() sheetState = sheetState,
}, modifier = modifier,
sheetState = sheetState, shape = RoundedCornerShape(topStart = MaterialTheme.dime.lg, topEnd = MaterialTheme.dime.lg)
modifier = modifier, ) {
shape = RoundedCornerShape(topStart = MaterialTheme.dime.lg, topEnd = MaterialTheme.dime.lg) Box(
) { modifier = Modifier
Box( .fillMaxWidth()
modifier = Modifier ) {
.fillMaxWidth() content()
) { }
content() }
} }
}
}
@@ -1,26 +1,25 @@
package com.approagency.drug.presentation.common package com.approagency.pharmacy.presentation.common
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clip
@Composable
@Composable
fun CustomBox(
fun CustomBox( modifier: Modifier? = Modifier,
modifier: Modifier? = Modifier, child: @Composable ()-> Unit
child: @Composable ()-> Unit ) {
) { if (modifier != null) {
if (modifier != null) { Box(
Box( modifier = modifier.clip(shape = MaterialTheme.shapes.large).background(
modifier = modifier.clip(shape = MaterialTheme.shapes.large).background( color = MaterialTheme.colorScheme.surfaceContainer
color = MaterialTheme.colorScheme.surfaceContainer )
) ) {
) { child()
child() }
} }
}
} }
@@ -1,40 +1,39 @@
package com.approagency.drug.presentation.common package com.approagency.pharmacy.presentation.common
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface
import androidx.compose.material3.Surface import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.Dialog import com.vada.caller.ui.theme.dime
import com.vada.caller.ui.theme.dime
@Composable
@Composable fun CustomModalDialog(
fun CustomModalDialog( onDismissRequest: () -> Unit,
onDismissRequest: () -> Unit, modifier: Modifier = Modifier,
modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit
content: @Composable ColumnScope.() -> Unit ) {
) { Dialog(
Dialog( onDismissRequest = onDismissRequest
onDismissRequest = onDismissRequest ){
){ Surface(
Surface( modifier = modifier
modifier = modifier .wrapContentHeight(),
.wrapContentHeight(), shape = MaterialTheme.shapes.medium,
shape = MaterialTheme.shapes.medium, color = MaterialTheme.colorScheme.surface,
color = MaterialTheme.colorScheme.surface, tonalElevation = MaterialTheme.dime.sm
tonalElevation = MaterialTheme.dime.sm ) {
) { Column(
Column( modifier = Modifier
modifier = Modifier .padding(MaterialTheme.dime.lg),
.padding(MaterialTheme.dime.lg), verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.lg),
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.lg), content = content
content = content )
) }
} }
}
} }
@@ -1,183 +1,182 @@
package com.approagency.drug.presentation.common package com.approagency.pharmacy.presentation.common
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
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.size
import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
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.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember 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.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.dp import com.vada.caller.ui.theme.dime
import com.vada.caller.ui.theme.dime
@Composable
@Composable fun CustomTextFilled(
fun CustomTextFilled( value: String = "",
value: String = "", onValueChange: (String) -> Unit,
onValueChange: (String) -> Unit, onSearch: (String) -> Unit = {},
onSearch: (String) -> Unit = {}, placeholder: String = "جستجو...",
placeholder: String = "جستجو...", modifier: Modifier = Modifier,
modifier: Modifier = Modifier, enabled: Boolean = true,
enabled: Boolean = true, readOnly: Boolean = false,
readOnly: Boolean = false, singleLine: Boolean = true,
singleLine: Boolean = true, showClearButton: Boolean = true,
showClearButton: Boolean = true, showSearchButton: Boolean = true,
showSearchButton: Boolean = true, autoSearch: Boolean = false,
autoSearch: Boolean = false, searchDelay: Long = 1200L,
searchDelay: Long = 1200L, keyboardOptions: KeyboardOptions = KeyboardOptions(
keyboardOptions: KeyboardOptions = KeyboardOptions( imeAction = ImeAction.Search
imeAction = ImeAction.Search ),
), keyboardActions: KeyboardActions = KeyboardActions(
keyboardActions: KeyboardActions = KeyboardActions( onSearch = {
onSearch = { if (value.isNotBlank()) {
if (value.isNotBlank()) { onSearch(value)
onSearch(value) }
} }
} ),
), textStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.bodyLarge,
textStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.bodyLarge, hintStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.bodyMedium.copy(
hintStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.colorScheme.onSurfaceVariant
color = MaterialTheme.colorScheme.onSurfaceVariant ),
), containerColor: androidx.compose.ui.graphics.Color = MaterialTheme.colorScheme.surfaceVariant,
containerColor: androidx.compose.ui.graphics.Color = MaterialTheme.colorScheme.surfaceVariant, borderColor: androidx.compose.ui.graphics.Color = MaterialTheme.colorScheme.primaryContainer,
borderColor: androidx.compose.ui.graphics.Color = MaterialTheme.colorScheme.primaryContainer, shape: androidx.compose.ui.graphics.Shape = MaterialTheme.shapes.medium,
shape: androidx.compose.ui.graphics.Shape = MaterialTheme.shapes.medium, height: Int = 45,
height: Int = 45, onFocusChange: ((Boolean) -> Unit)? = null
onFocusChange: ((Boolean) -> Unit)? = null ) {
) { var isFocused by remember { mutableStateOf(false) }
var isFocused by remember { mutableStateOf(false) } var localValue by remember(value) { mutableStateOf(value) }
var localValue by remember(value) { mutableStateOf(value) }
// Auto-search functionality
// Auto-search functionality androidx.compose.runtime.LaunchedEffect(autoSearch, searchDelay, localValue) {
androidx.compose.runtime.LaunchedEffect(autoSearch, searchDelay, localValue) { if (autoSearch && localValue.isNotBlank()) {
if (autoSearch && localValue.isNotBlank()) { kotlinx.coroutines.delay(searchDelay)
kotlinx.coroutines.delay(searchDelay) onSearch(localValue)
onSearch(localValue) }
} }
}
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { Box(
Box( modifier = modifier
modifier = modifier .fillMaxWidth()
.fillMaxWidth() .height(height.dp)
.height(height.dp) .background(
.background( color = containerColor,
color = containerColor, shape = shape
shape = shape )
) .border(
.border( width = 1.dp,
width = 1.dp, color = if (isFocused)
color = if (isFocused) MaterialTheme.colorScheme.primary
MaterialTheme.colorScheme.primary else
else borderColor,
borderColor, shape = shape
shape = shape )
) .padding(horizontal = MaterialTheme.dime.md, vertical = MaterialTheme.dime.sm)
.padding(horizontal = MaterialTheme.dime.md, vertical = MaterialTheme.dime.sm) .onFocusChanged { focusState ->
.onFocusChanged { focusState -> isFocused = focusState.isFocused
isFocused = focusState.isFocused onFocusChange?.invoke(focusState.isFocused)
onFocusChange?.invoke(focusState.isFocused) }
} ) {
) { Row(
Row( verticalAlignment = Alignment.CenterVertically,
verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()
modifier = Modifier.fillMaxWidth() ) {
) { // Clear button (shows when there's text and clear button is enabled)
// Clear button (shows when there's text and clear button is enabled) if (showClearButton && localValue.isNotEmpty()) {
if (showClearButton && localValue.isNotEmpty()) { IconButton(
IconButton( onClick = {
onClick = { localValue = ""
localValue = "" onValueChange("")
onValueChange("") },
}, modifier = Modifier.size(28.dp)
modifier = Modifier.size(28.dp) ) {
) { Icon(
Icon( imageVector = Icons.Default.Close,
imageVector = Icons.Default.Close, contentDescription = "Clear",
contentDescription = "Clear", modifier = Modifier.size(16.dp),
modifier = Modifier.size(16.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant
tint = MaterialTheme.colorScheme.onSurfaceVariant )
) }
} }
}
// Text field
// Text field BasicTextField(
BasicTextField( value = localValue,
value = localValue, onValueChange = { newValue ->
onValueChange = { newValue -> localValue = newValue
localValue = newValue onValueChange(newValue)
onValueChange(newValue) },
}, modifier = Modifier
modifier = Modifier .weight(1f)
.weight(1f) .padding(horizontal = if (showClearButton && localValue.isNotEmpty()) 4.dp else 0.dp),
.padding(horizontal = if (showClearButton && localValue.isNotEmpty()) 4.dp else 0.dp), enabled = enabled,
enabled = enabled, readOnly = readOnly,
readOnly = readOnly, singleLine = singleLine,
singleLine = singleLine, textStyle = textStyle,
textStyle = textStyle, keyboardOptions = keyboardOptions,
keyboardOptions = keyboardOptions, keyboardActions = keyboardActions,
keyboardActions = keyboardActions, decorationBox = { innerTextField ->
decorationBox = { innerTextField -> Box(
Box( modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.CenterStart
contentAlignment = Alignment.CenterStart ) {
) { if (localValue.isEmpty() && !isFocused) {
if (localValue.isEmpty() && !isFocused) { Text(
Text( text = placeholder,
text = placeholder, style = hintStyle
style = hintStyle )
) }
} innerTextField()
innerTextField() }
} }
} )
)
// Search button
// Search button if (showSearchButton) {
if (showSearchButton) { IconButton(
IconButton( onClick = {
onClick = { if (localValue.isNotBlank()) {
if (localValue.isNotBlank()) { onSearch(localValue)
onSearch(localValue) }
} },
}, modifier = Modifier.size(32.dp)
modifier = Modifier.size(32.dp) ) {
) { Icon(
Icon( imageVector = Icons.Default.Search,
imageVector = Icons.Default.Search, contentDescription = "Search",
contentDescription = "Search", modifier = Modifier.size(18.dp),
modifier = Modifier.size(18.dp), tint = if (localValue.isNotBlank())
tint = if (localValue.isNotBlank()) MaterialTheme.colorScheme.primary
MaterialTheme.colorScheme.primary else
else MaterialTheme.colorScheme.onSurfaceVariant
MaterialTheme.colorScheme.onSurfaceVariant )
) }
} }
} }
} }
} }
}
} }
@@ -1,4 +1,4 @@
package com.approagency.drug.presentation.common package com.approagency.pharmacy.presentation.common
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -10,7 +10,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.vada.caller.ui.theme.LocalDime import com.vada.caller.ui.theme.LocalDime
@@ -1,4 +1,4 @@
package com.approagency.drug.presentation.common package com.approagency.pharmacy.presentation.common
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@@ -1,21 +1,21 @@
package com.approagency.drug.presentation.common package com.approagency.pharmacy.presentation.common
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
object SnackbarManager { object SnackbarManager {
private var snackbarHostState: SnackbarHostState? = null private var snackbarHostState: SnackbarHostState? = null
private var coroutineScope: CoroutineScope? = null private var coroutineScope: CoroutineScope? = null
fun init(scope: CoroutineScope, hostState: SnackbarHostState) { fun init(scope: CoroutineScope, hostState: SnackbarHostState) {
coroutineScope = scope coroutineScope = scope
snackbarHostState = hostState snackbarHostState = hostState
} }
fun showMessage(message: String) { fun showMessage(message: String) {
coroutineScope?.launch { coroutineScope?.launch {
snackbarHostState?.showSnackbar(message) snackbarHostState?.showSnackbar(message)
} }
} }
} }
@@ -1,7 +1,7 @@
package com.vada.caller.presentation.common package com.vada.caller.presentation.common
sealed interface UiState<out T> { sealed interface UiState<out T> {
object Loading : UiState<Nothing> object Loading : UiState<Nothing>
data class Success<T>(val data: T) : UiState<T> data class Success<T>(val data: T) : UiState<T>
data class Error(val message: String) : UiState<Nothing> data class Error(val message: String) : UiState<Nothing>
} }
@@ -1,25 +1,20 @@
package com.approagency.drug.presentation.common package com.approagency.pharmacy.presentation.common
import android.content.res.Resources.Theme import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.foundation.layout.height import androidx.compose.material3.MaterialTheme
import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.ui.Modifier
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.ui.unit.dp
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable @Composable
import androidx.compose.ui.Modifier fun Loading(
import androidx.compose.ui.unit.dp color: Color = MaterialTheme.colorScheme.onPrimary,
import com.vada.caller.ui.theme.dime modifier: Modifier = Modifier
) {
@Composable CircularProgressIndicator(
fun Loading( color = color,
color: Color = MaterialTheme.colorScheme.onPrimary, modifier = modifier,
modifier: Modifier = Modifier strokeWidth = 2.dp
) { )
CircularProgressIndicator(
color = color,
modifier = modifier,
strokeWidth = 2.dp
)
} }
@@ -1,39 +1,38 @@
package com.approagency.drug.presentation.common package com.approagency.pharmacy.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 import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme
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.text.style.TextAlign import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.dp
@Composable
@Composable fun PrimaryButton(
fun PrimaryButton( text: String,
text: String, isLoading: Boolean?,
isLoading: Boolean?, onClick: () -> Unit,
onClick: () -> Unit, height: Int? = null
height: Int? = null ) {
) { Button(
Button( colors = ButtonDefaults.buttonColors(
colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.primary,
containerColor = MaterialTheme.colorScheme.primary, contentColor = MaterialTheme.colorScheme.onPrimary
contentColor = MaterialTheme.colorScheme.onPrimary ),
), shape = MaterialTheme.shapes.medium,
shape = MaterialTheme.shapes.medium, modifier = Modifier.fillMaxWidth().height(height?.dp ?: 50.dp),
modifier = Modifier.fillMaxWidth().height(height?.dp ?: 50.dp), onClick = {
onClick = { onClick()
onClick() },
}, ) {
) { if (isLoading == true) {
if (isLoading == true) { Text("... در حال جستجو" , textAlign = TextAlign.Right)
Text("... در حال جستجو" , textAlign = TextAlign.Right) } else {
} else { Text(text)
Text(text) }
} }
}
} }
@@ -1,131 +1,122 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons
import androidx.compose.foundation.layout.width import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Icon
import androidx.compose.material.icons.Icons import androidx.compose.material3.MaterialTheme
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft import androidx.compose.material3.Text
import androidx.compose.material.icons.filled.Search import androidx.compose.material3.TextButton
import androidx.compose.material3.Icon import androidx.compose.runtime.Composable
import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.remember
import androidx.compose.material3.Text import androidx.compose.ui.AbsoluteAlignment
import androidx.compose.material3.TextButton import androidx.compose.ui.Alignment
import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier
import androidx.compose.runtime.remember import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.AbsoluteAlignment import com.approagency.pharmacy.data.dto.DarmanList
import androidx.compose.ui.Alignment import com.approagency.pharmacy.data.dto.DarmanModel
import androidx.compose.ui.Modifier import com.approagency.pharmacy.presentation.common.CustomBox
import androidx.compose.ui.text.font.FontWeight import com.vada.caller.ui.theme.dime
import androidx.compose.ui.text.style.TextAlign import kotlin.collections.emptyList
import androidx.compose.ui.unit.dp
import com.approagency.drug.data.dto.DarmanList @Composable
import com.approagency.drug.data.dto.DarmanModel fun DarmanContent(
import com.approagency.drug.data.dto.DrugDetail darmanData: Result<DarmanModel?>?,
import com.approagency.drug.data.dto.DrugListResponse onDarmanClick: (Int) -> Unit,
import com.approagency.drug.presentation.common.CustomBox onRetryClick:()-> Unit,
import com.approagency.drug.presentation.common.PrimaryButton ){
import com.vada.caller.ui.theme.dime val drugs = darmanData?.getOrNull()?.data ?: emptyList()
import kotlin.collections.emptyList if (drugs.isEmpty()){
RetryContent(
@Composable modifier = Modifier,
fun DarmanContent( error = "مشکلی از سمت سرور پیش آمده",
darmanData: Result<DarmanModel?>?, onClick = {
onDarmanClick: (Int) -> Unit, onRetryClick()
onRetryClick:()-> Unit, }
){ )
val drugs = darmanData?.getOrNull()?.data ?: emptyList() } else {
if (drugs.isEmpty()){ LazyColumn {
RetryContent( items(drugs.count() , itemContent ={
modifier = Modifier, i -> DarmanItem(
error = "مشکلی از سمت سرور پیش آمده", darman = darmanData?.getOrNull()!!.data[i] ,
onClick = { onClick = { onDarmanClick(drugs[i].cod!!) }
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(
@Composable indication = null,
fun DarmanItem( interactionSource = remember { MutableInteractionSource() }
darman: DarmanList, // Adjust this based on your actual data class ) {
onClick: (Int?) -> Unit onClick(darman.cod)
) { },
CustomBox( child = {
modifier = Modifier Row(
.padding(bottom = MaterialTheme.dime.sm) modifier = Modifier.padding(all = MaterialTheme.dime.lg),
.clickable( ) {
indication = null, Column (
interactionSource = remember { MutableInteractionSource() } verticalArrangement = Arrangement.Center,
) { horizontalAlignment = Alignment.Start
onClick(darman.cod) ) {
}, Column(
child = { modifier = Modifier.fillMaxWidth(),
Row( horizontalAlignment = AbsoluteAlignment.Right
modifier = Modifier.padding(all = MaterialTheme.dime.lg), // horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Column ( Text(darman.nam_fa.toString() , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right )
verticalArrangement = Arrangement.Center, Text(darman.nam_en.toString() , style = MaterialTheme.typography.bodySmall)
horizontalAlignment = Alignment.Start }
) { Row (
Column( modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center
horizontalAlignment = AbsoluteAlignment.Right ) {
// horizontalArrangement = Arrangement.SpaceBetween TextButton(
) { onClick = {
Text(darman.nam_fa.toString() , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right ) onClick(darman.cod)
Text(darman.nam_en.toString() , style = MaterialTheme.typography.bodySmall) }
} ) {
Row ( Row(
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center
horizontalArrangement = Arrangement.Center ) {
) { Icon(
TextButton( imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
onClick = { , contentDescription = "see more"
onClick(darman.cod) )
} Text("مشاهده لیست")
) { }
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
} // }
)
// Button(
// onClick = onClick,
// modifier = Modifier
// .fillMaxWidth()
// .padding(vertical = 4.dp)
// ) {
// drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model
// }
} }
@@ -1,4 +1,4 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -6,20 +6,15 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.approagency.drug.domain.model.DaroYabSearchResult import com.approagency.pharmacy.domain.model.DrugSearchResult
import com.approagency.drug.domain.model.DrugSearchResult import com.approagency.pharmacy.presentation.common.CustomBox
import com.approagency.drug.presentation.common.CustomBox
import com.vada.caller.ui.theme.LocalDime import com.vada.caller.ui.theme.LocalDime
import com.vada.caller.ui.theme.dime import com.vada.caller.ui.theme.dime
@@ -1,152 +1,152 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
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.automirrored.twotone.KeyboardArrowLeft
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import com.approagency.drug.data.dto.DrugDetail import com.approagency.pharmacy.data.dto.DrugDetail
import com.approagency.drug.data.dto.DrugListResponse import com.approagency.pharmacy.data.dto.DrugListResponse
import com.approagency.drug.presentation.common.CustomBox import com.approagency.pharmacy.presentation.common.CustomBox
import com.vada.caller.ui.theme.dime import com.vada.caller.ui.theme.dime
@Composable @Composable
fun DrugListContent( fun DrugListContent(
drugsData: Result<DrugListResponse?>?, drugsData: Result<DrugListResponse?>?,
onDrugClick: (Int) -> Unit onDrugClick: (Int) -> Unit
) { ) {
val drugs = drugsData?.getOrNull()?.data ?: emptyList() val drugs = drugsData?.getOrNull()?.data ?: emptyList()
if (drugs.isEmpty()) { if (drugs.isEmpty()) {
CustomBox( CustomBox(
child = { child = {
Row( Row(
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(all = MaterialTheme.dime.sm), .padding(all = MaterialTheme.dime.sm),
) { ) {
Text( Text(
text = "نتیجه‌ای یافت نشد", text = "نتیجه‌ای یافت نشد",
fontWeight = FontWeight.W500 fontWeight = FontWeight.W500
) )
Spacer(modifier = Modifier.width(MaterialTheme.dime.xs)) Spacer(modifier = Modifier.width(MaterialTheme.dime.xs))
Icon( Icon(
imageVector = Icons.Default.Search, imageVector = Icons.Default.Search,
contentDescription = "Search" contentDescription = "Search"
) )
} }
} }
) )
} else { } else {
LazyColumn( LazyColumn(
){ ){
items(drugs.count() , itemContent ={ items(drugs.count() , itemContent ={
i -> DrugItem( i -> DrugItem(
drug = drugs[i], drug = drugs[i],
onClick = { onDrugClick(drugs[i].cod!!) } onClick = { onDrugClick(drugs[i].cod!!) }
) )
} ) } )
} }
} }
} }
@Composable @Composable
fun DrugItem( fun DrugItem(
drug: DrugDetail, // Adjust this based on your actual data class drug: DrugDetail, // Adjust this based on your actual data class
onClick: (Int?) -> Unit onClick: (Int?) -> Unit
) { ) {
CustomBox( CustomBox(
modifier = Modifier modifier = Modifier
.padding(bottom = MaterialTheme.dime.sm) .padding(bottom = MaterialTheme.dime.sm)
.fillMaxWidth() .fillMaxWidth()
.clickable( .clickable(
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
onClick(drug.cod) onClick(drug.cod)
}, },
child = { child = {
Row( Row(
modifier = Modifier.padding(all = MaterialTheme.dime.lg), modifier = Modifier.padding(all = MaterialTheme.dime.lg),
) { ) {
Column ( Column (
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.Start horizontalAlignment = Alignment.Start
) { ) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Text(drug.nam_en.toString() , style = MaterialTheme.typography.bodySmall) Text(drug.nam_en.toString() , style = MaterialTheme.typography.bodySmall)
Text(drug.nam_fa.toString() , style = MaterialTheme.typography.labelLarge,textAlign = TextAlign.Right) Text(drug.nam_fa.toString() , style = MaterialTheme.typography.labelLarge,textAlign = TextAlign.Right)
} }
if (drug.goroh_darmani != null ) if (drug.goroh_darmani != null )
Row( Row(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
, horizontalArrangement = Arrangement.End , horizontalArrangement = Arrangement.End
) {Text( ) {Text(
"گروه دارویی: ${drug.goroh_darmani.nam_fa.toString()}", "گروه دارویی: ${drug.goroh_darmani.nam_fa.toString()}",
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
textAlign = TextAlign.Right textAlign = TextAlign.Right
) } ) }
Row ( Row (
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
TextButton( TextButton(
onClick = { onClick = {
onClick(drug.cod) onClick(drug.cod)
} }
) { ) {
Row( Row(
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
Icon( Icon(
imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
, contentDescription = "see more" , contentDescription = "see more"
) )
Text("مشاهده جزییات") Text("مشاهده جزییات")
} }
} }
} }
} }
} }
} }
) )
// Button( // Button(
// onClick = onClick, // onClick = onClick,
// modifier = Modifier // modifier = Modifier
// .fillMaxWidth() // .fillMaxWidth()
// .padding(vertical = 4.dp) // .padding(vertical = 4.dp)
// ) { // ) {
// drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model // drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model
// } // }
} }
@@ -1,40 +1,40 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme 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.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.approagency.drug.presentation.common.CustomBox import com.approagency.pharmacy.presentation.common.CustomBox
import com.vada.caller.ui.theme.dime import com.vada.caller.ui.theme.dime
@Composable @Composable
fun EmptyStateContent() { fun EmptyStateContent() {
Column( Column(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
, verticalArrangement = Arrangement.Center , verticalArrangement = Arrangement.Center
) { ) {
Icon( Icon(
imageVector = Icons.Default.Search, imageVector = Icons.Default.Search,
contentDescription = "Search", contentDescription = "Search",
modifier = Modifier.size(MaterialTheme.dime.iconLg) modifier = Modifier.size(MaterialTheme.dime.iconLg)
) )
CustomBox( CustomBox(
child = { child = {
Text( Text(
text = "برای جستجو، نام دارو را وارد کنید", text = "برای جستجو، نام دارو را وارد کنید",
modifier = Modifier.padding(MaterialTheme.dime.sm) modifier = Modifier.padding(MaterialTheme.dime.sm)
) )
} }
) )
} }
} }
@@ -1,4 +1,4 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -1,4 +1,4 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import android.annotation.SuppressLint import android.annotation.SuppressLint
@@ -13,23 +13,21 @@ import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
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.LocalConfiguration
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.approagency.drug.domain.model.PharmacyItem import com.approagency.pharmacy.domain.model.PharmacyItem
import com.approagency.drug.presentation.common.CustomBox import com.approagency.pharmacy.presentation.common.CustomBox
import com.approagency.drug.presentation.common.CustomModalBottomSheet import com.approagency.pharmacy.presentation.common.CustomModalBottomSheet
import com.approagency.drug.presentation.common.Loading import com.approagency.pharmacy.presentation.common.Loading
import com.approagency.drug.presentation.viewModel.PharmacyDetailState import com.approagency.pharmacy.presentation.viewModel.PharmacyDetailState
import com.approagency.drug.presentation.viewModel.PharmacyState import com.approagency.pharmacy.presentation.viewModel.PharmacyState
import com.approagency.drug.presentation.viewModel.PharmacyViewModel import com.approagency.pharmacy.presentation.viewModel.PharmacyViewModel
import com.approagency.drug.utils.provinces import com.approagency.pharmacy.utils.provinces
import com.vada.caller.ui.theme.LocalDime import com.vada.caller.ui.theme.LocalDime
import com.vada.caller.ui.theme.dime import com.vada.caller.ui.theme.dime
import kotlinx.coroutines.launch
@SuppressLint("ConfigurationScreenWidthHeight") @SuppressLint("ConfigurationScreenWidthHeight")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -1,4 +1,4 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -8,8 +8,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.approagency.drug.presentation.common.CustomBox import com.approagency.pharmacy.presentation.common.CustomBox
import com.approagency.drug.presentation.common.PrimaryButton import com.approagency.pharmacy.presentation.common.PrimaryButton
import com.vada.caller.ui.theme.dime import com.vada.caller.ui.theme.dime
@Composable @Composable
@@ -1,205 +1,204 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
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.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize 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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme 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.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.approagency.drug.domain.model.TestGroup import com.approagency.pharmacy.domain.model.TestGroup
import com.approagency.drug.domain.model.TestItem import com.approagency.pharmacy.domain.model.TestItem
import com.approagency.drug.presentation.common.CustomBox import com.approagency.pharmacy.presentation.common.CustomBox
import com.approagency.drug.presentation.common.Loading import com.approagency.pharmacy.presentation.common.Loading
import com.approagency.drug.presentation.viewModel.SearchResult import com.approagency.pharmacy.presentation.viewModel.SearchResult
import com.vada.caller.ui.theme.dime import com.vada.caller.ui.theme.dime
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.ui.AbsoluteAlignment import androidx.compose.ui.AbsoluteAlignment
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.text.style.TextOverflow
@Composable
@Composable fun SearchResultsContent(
fun SearchResultsContent( searchResult: SearchResult?,
searchResult: SearchResult?, isLoading: Boolean,
isLoading: Boolean, error: String?,
error: String?, onGroupClick: (TestGroup) -> Unit,
onGroupClick: (TestGroup) -> Unit, onItemClick: (TestItem) -> Unit,
onItemClick: (TestItem) -> Unit, onClearSearch: () -> Unit
onClearSearch: () -> Unit ) {
) { when {
when { isLoading -> {
isLoading -> { Box(
Box( modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center
contentAlignment = Alignment.Center ) {
) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Loading(color = MaterialTheme.colorScheme.primary)
Loading(color = MaterialTheme.colorScheme.primary) Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm)) Text("در حال جستجو...")
Text("در حال جستجو...") }
} }
} }
}
error != null -> {
error != null -> { Box(
Box( modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center
contentAlignment = Alignment.Center ) {
) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Text(
Text( text = "خطا در جستجو",
text = "خطا در جستجو", style = MaterialTheme.typography.bodyLarge,
style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.error
color = MaterialTheme.colorScheme.error )
) Text(
Text( text = error,
text = error, style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodySmall, modifier = Modifier.padding(vertical = 8.dp)
modifier = Modifier.padding(vertical = 8.dp) )
) }
} }
} }
}
searchResult != null -> {
searchResult != null -> { val groups = searchResult.groups
val groups = searchResult.groups val items = searchResult.items
val items = searchResult.items
if (groups.isEmpty() && items.isEmpty()) {
if (groups.isEmpty() && items.isEmpty()) { Box(
Box( modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center
contentAlignment = Alignment.Center ) {
) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Text(
Text( text = "نتیجه‌ای یافت نشد",
text = "نتیجه‌ای یافت نشد", style = MaterialTheme.typography.titleMedium
style = MaterialTheme.typography.titleMedium )
) Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm)) Text(
Text( text = "لطفاً عبارت دیگری را جستجو کنید",
text = "لطفاً عبارت دیگری را جستجو کنید", style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant
color = MaterialTheme.colorScheme.onSurfaceVariant )
) }
} }
} } else {
} else { LazyColumn(
LazyColumn( modifier = Modifier
modifier = Modifier .fillMaxSize()
.fillMaxSize() .padding(vertical = MaterialTheme.dime.md),
.padding(vertical = MaterialTheme.dime.md), horizontalAlignment = AbsoluteAlignment.Right
horizontalAlignment = AbsoluteAlignment.Right
) {
) { if (groups.isNotEmpty()) {
if (groups.isNotEmpty()) { item {
item { Text(
Text( text = "گروه‌های آزمایشگاهی (${groups.size})",
text = "گروه‌های آزمایشگاهی (${groups.size})", style = MaterialTheme.typography.titleMedium.copy(
style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center
textAlign = TextAlign.Center ),
), fontWeight = FontWeight.Bold,
fontWeight = FontWeight.Bold, modifier = Modifier.padding(vertical = MaterialTheme.dime.md)
modifier = Modifier.padding(vertical = MaterialTheme.dime.md) )
) }
}
items(groups) { group ->
items(groups) { group -> TestGroupItemContent(
TestGroupItemContent( group = group,
group = group, onClick = { onGroupClick(group) }
onClick = { onGroupClick(group) } )
) }
}
if (items.isNotEmpty()) {
if (items.isNotEmpty()) { item {
item { HorizontalDivider(
HorizontalDivider( modifier = Modifier.padding(vertical = MaterialTheme.dime.md)
modifier = Modifier.padding(vertical = MaterialTheme.dime.md) )
) Text(
Text( text = "آیتم‌های آزمایشگاهی (${items.size})",
text = "آیتم‌های آزمایشگاهی (${items.size})", style = MaterialTheme.typography.titleMedium,
style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold,
fontWeight = FontWeight.Bold, modifier = Modifier.padding(vertical = MaterialTheme.dime.md)
modifier = Modifier.padding(vertical = MaterialTheme.dime.md) )
) }
} }
} }
}
items(items) { item ->
items(items) { item -> TestItemSearchResult(
TestItemSearchResult( testItem = item,
testItem = item, onClick = { onItemClick(item) }
onClick = { onItemClick(item) } )
) }
} }
} }
} }
} }
} }
}
@Composable
@Composable fun TestItemSearchResult(
fun TestItemSearchResult( testItem: TestItem,
testItem: TestItem, onClick: () -> Unit
onClick: () -> Unit ) {
) { CustomBox(
CustomBox( modifier = Modifier
modifier = Modifier .fillMaxWidth()
.fillMaxWidth() .padding(bottom = MaterialTheme.dime.sm)
.padding(bottom = MaterialTheme.dime.sm) .clickable(
.clickable( indication = null,
indication = null, interactionSource = remember { MutableInteractionSource() }
interactionSource = remember { MutableInteractionSource() } ) {
) { onClick()
onClick() }
} ) {
) { Column(
Column( modifier = Modifier.padding(all = MaterialTheme.dime.lg),
modifier = Modifier.padding(all = MaterialTheme.dime.lg), horizontalAlignment = AbsoluteAlignment.Right
horizontalAlignment = AbsoluteAlignment.Right ) {
) { Text(
Text( text = testItem.title ?: "بدون عنوان",
text = testItem.title ?: "بدون عنوان", style = MaterialTheme.typography.labelLarge,
style = MaterialTheme.typography.labelLarge, fontWeight = FontWeight.SemiBold
fontWeight = FontWeight.SemiBold )
)
if (!testItem.normalValue.isNullOrBlank()) {
if (!testItem.normalValue.isNullOrBlank()) { Text(
Text( text = "مقدار نرمال: ${testItem.normalValue}",
text = "مقدار نرمال: ${testItem.normalValue}", style = MaterialTheme.typography.bodySmall.copy(
style = MaterialTheme.typography.bodySmall.copy( textDirection = TextDirection.Rtl,
textDirection = TextDirection.Rtl, textAlign = TextAlign.Right
textAlign = TextAlign.Right ),
), color = MaterialTheme.colorScheme.primary,
color = MaterialTheme.colorScheme.primary, modifier = Modifier.padding(top = MaterialTheme.dime.xs)
modifier = Modifier.padding(top = MaterialTheme.dime.xs) )
) }
}
if (!testItem.detail.isNullOrBlank()) {
if (!testItem.detail.isNullOrBlank()) { Text(
Text( text = testItem.detail,
text = testItem.detail, style = MaterialTheme.typography.bodySmall.copy(
style = MaterialTheme.typography.bodySmall.copy( textDirection = TextDirection.Rtl,
textDirection = TextDirection.Rtl, textAlign = TextAlign.Right
textAlign = TextAlign.Right ),
), color = MaterialTheme.colorScheme.onSurfaceVariant,
color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(top = MaterialTheme.dime.sm)
modifier = Modifier.padding(top = MaterialTheme.dime.sm) )
) }
} }
} }
} }
}
@@ -1,225 +1,222 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.foundation.lazy.items // Import items if you were to use it for lists within LazyColumn import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable
import androidx.compose.material3.Text import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.text.style.TextAlign import com.approagency.pharmacy.data.dto.DrugDetail // Import DrugDetail directly
import androidx.compose.ui.unit.LayoutDirection import com.approagency.pharmacy.presentation.common.CustomModalBottomSheet
import com.approagency.drug.data.dto.DrugDetail // Import DrugDetail directly import com.approagency.pharmacy.presentation.viewModel.DrugDetailState
import com.approagency.drug.data.dto.DrugModels import com.approagency.pharmacy.presentation.viewModel.HomeViewModel
import com.approagency.drug.presentation.common.CustomModalBottomSheet import com.vada.caller.ui.theme.dime // Assuming this is your custom spacing/dimension object
import com.approagency.drug.presentation.viewModel.DrugDetailState
import com.approagency.drug.presentation.viewModel.HomeViewModel @Composable
import com.vada.caller.ui.theme.dime // Assuming this is your custom spacing/dimension object @OptIn(ExperimentalMaterial3Api::class)
fun DrugDetailSheet(
@Composable viewModel: HomeViewModel,
@OptIn(ExperimentalMaterial3Api::class) state: DrugDetailState // Assuming you pass the state containing the detail
fun DrugDetailSheet( ) {
viewModel: HomeViewModel, val detail: DrugDetail? = state.drugDetail?.getOrNull()?.data
state: DrugDetailState // Assuming you pass the state containing the detail
) { // Only show the sheet if detail is not null
val detail: DrugDetail? = state.drugDetail?.getOrNull()?.data if (detail != null) {
CustomModalBottomSheet(
// Only show the sheet if detail is not null onDismiss = {
if (detail != null) { viewModel.closeDetail()
CustomModalBottomSheet( },
onDismiss = { content = {
viewModel.closeDetail() // Use a LazyColumn for scrollable content inside the sheet
},
content = { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
// Use a LazyColumn for scrollable content inside the sheet LazyColumn(
modifier = Modifier
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { .fillMaxWidth()
LazyColumn( .padding(horizontal = MaterialTheme.dime.lg),
modifier = Modifier contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg) // Add padding to the bottom of the content for better scrolling
.fillMaxWidth() ) {
.padding(horizontal = MaterialTheme.dime.lg), // --- 1. Main Title ---
contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg) // Add padding to the bottom of the content for better scrolling item { // Use item {} for single composables within LazyColumn
) { Text(
// --- 1. Main Title --- text = detail.nam_fa ?: "جزئیات دارو",
item { // Use item {} for single composables within LazyColumn style = MaterialTheme.typography.headlineMedium,
Text( fontWeight = FontWeight.Bold,
text = detail.nam_fa ?: "جزئیات دارو", modifier = Modifier.padding(bottom = MaterialTheme.dime.md)
style = MaterialTheme.typography.headlineMedium, )
fontWeight = FontWeight.Bold, Text(
modifier = Modifier.padding(bottom = MaterialTheme.dime.md) text = detail.nam_en ?: "N/A (English Name)",
) style = MaterialTheme.typography.titleMedium,
Text( color = MaterialTheme.colorScheme.onSurfaceVariant,
text = detail.nam_en ?: "N/A (English Name)", modifier = Modifier.padding(bottom = MaterialTheme.dime.lg)
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))
}
// Divider for visual separation
HorizontalDivider(Modifier.padding(bottom = MaterialTheme.dime.md)) // --- 2. Key Groupings ---
} // Goroh Darmani (Pharmacological Group)
detail.goroh_darmani?.let { group ->
// --- 2. Key Groupings --- item {
// Goroh Darmani (Pharmacological Group) DetailRow(
detail.goroh_darmani?.let { group -> title = "گروه درمانی",
item { value = group.nam_fa ?: "نامشخص"
DetailRow( )
title = "گروه درمانی", }
value = group.nam_fa ?: "نامشخص" group.nam_en?.let { enName ->
) item {
} DetailRow(
group.nam_en?.let { enName -> title = "Group (EN)",
item { value = enName
DetailRow( )
title = "Group (EN)", }
value = enName }
) }
}
} // Goroh Darmanei (Therapeutic Group)
} detail.goroh_daroei?.let { group ->
item {
// Goroh Darmanei (Therapeutic Group) DetailRow(
detail.goroh_daroei?.let { group -> title = "گروه دارویی",
item { value = group.nam ?: "نامشخص"
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
// Goroh Farmakologic (Pharmacological Class) // if cod needs to be translated to a name. For now, we show the code.
detail.goroh_farmakologic_cod?.let { cod -> item {
// NOTE: You might need a separate API call or mapping here DetailRow(
// if cod needs to be translated to a name. For now, we show the code. title = "کلاس فارماکولوژیک",
item { value = cod.toString()
DetailRow( )
title = "کلاس فارماکولوژیک", }
value = cod.toString() }
)
} item { // Add a divider as a separate item
} HorizontalDivider(Modifier.padding(vertical = MaterialTheme.dime.md))
}
item { // Add a divider as a separate item
HorizontalDivider(Modifier.padding(vertical = MaterialTheme.dime.md)) // --- 3. Usage & Mechanism xw---
} detail.mavaredmasraf?.let { usage ->
item {
// --- 3. Usage & Mechanism xw--- DetailSection(title = "موارد مصرف", content = usage)
detail.mavaredmasraf?.let { usage -> }
item { }
DetailSection(title = "موارد مصرف", content = usage)
} detail.mekanismtasir?.let { mechanism ->
} item {
DetailSection(title = "مکانیسم اثر", content = mechanism)
detail.mekanismtasir?.let { mechanism -> }
item { }
DetailSection(title = "مکانیسم اثر", content = mechanism)
} // --- 4. Warnings & Storage ---
} detail.hoshdar?.let { warning ->
item {
// --- 4. Warnings & Storage --- DetailSection(
detail.hoshdar?.let { warning -> title = "هشدارها",
item { content = warning,
DetailSection( isWarning = true
title = "هشدارها", )
content = warning, }
isWarning = true }
)
} detail.sharayetnegahdari?.let { storage ->
} item {
DetailSection(
detail.sharayetnegahdari?.let { storage -> title = "شرایط نگهداری",
item { content = storage
DetailSection( )
title = "شرایط نگهداری", }
content = storage }
) }
} }
} }
} )
} }
} }
)
} // --- Helper Composables for Clean Code ---
}
@Composable
// --- Helper Composables for Clean Code --- fun DetailRow(
title: String,
@Composable value: String
fun DetailRow( ) {
title: String, Row(
value: String modifier = Modifier
) { .fillMaxWidth()
Row( .padding(vertical = MaterialTheme.dime.sm),
modifier = Modifier horizontalArrangement = Arrangement.SpaceBetween
.fillMaxWidth() ) {
.padding(vertical = MaterialTheme.dime.sm), Text(
horizontalArrangement = Arrangement.SpaceBetween text = title,
) { style = MaterialTheme.typography.bodyMedium,
Text( color = MaterialTheme.colorScheme.onSurfaceVariant,
text = title, textAlign = TextAlign.Right
style = MaterialTheme.typography.bodyMedium, )
color = MaterialTheme.colorScheme.onSurfaceVariant, Text(
textAlign = TextAlign.Right text = value,
) style = MaterialTheme.typography.bodyLarge,
Text( fontWeight = FontWeight.SemiBold,
text = value, textAlign = TextAlign.End,
style = MaterialTheme.typography.bodyLarge, // Ensure text takes up available space and doesn't push other elements.
fontWeight = FontWeight.SemiBold, // Using weight(1f, fill = false) is good for aligning to the end if there's space.
textAlign = TextAlign.End, modifier = Modifier.weight(1f, fill = false)
// 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,
@Composable content: String,
fun DetailSection( isWarning: Boolean = false
title: String, ) {
content: String, Column(
isWarning: Boolean = false modifier = Modifier
) { .fillMaxWidth()
Column( .padding(vertical = MaterialTheme.dime.md)
modifier = Modifier ) {
.fillMaxWidth() Text(
.padding(vertical = MaterialTheme.dime.md) text = title,
) { style = MaterialTheme.typography.titleMedium,
Text( color = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
text = title, fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(bottom = MaterialTheme.dime.sm)
color = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary, )
fontWeight = FontWeight.Bold, // Use a text block that respects line breaks (usually done with \n)
modifier = Modifier.padding(bottom = MaterialTheme.dime.sm) Text(
) text = content,
// Use a text block that respects line breaks (usually done with \n) style = MaterialTheme.typography.bodyMedium,
Text( color = MaterialTheme.colorScheme.onSurface,
text = content, // Consider adding softWrap = true if content might overflow
style = MaterialTheme.typography.bodyMedium, // softWrap = true
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))
} }
// 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,
// Make sure your DrugDetailState in ViewModel looks something like this: // val drugDetail: DrugModels? = null, // Changed to DrugModels? to match data access
// data class DrugDetailState( // val isDetailVisible: Boolean = false,
// val isLoading: Boolean = false, // val error: String? = null
// val drugDetail: DrugModels? = null, // Changed to DrugModels? to match data access // )
// val isDetailVisible: Boolean = false,
// val error: String? = null
// )
@@ -1,122 +1,119 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.items import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme
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.runtime.CompositionLocalProvider
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue
import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.dp import com.approagency.pharmacy.domain.model.TestGroup
import com.approagency.drug.domain.model.TestGroup import com.approagency.pharmacy.presentation.common.CustomModalBottomSheet
import com.approagency.drug.presentation.common.CustomModalBottomSheet import com.approagency.pharmacy.presentation.viewModel.TestItemState
import com.approagency.drug.presentation.viewModel.LabViewModel import com.vada.caller.ui.theme.dime
import com.approagency.drug.presentation.viewModel.TestItemState
import com.vada.caller.ui.theme.dime @OptIn(ExperimentalMaterial3Api::class)
import kotlinx.coroutines.flow.collect @Composable
fun TestDetailSheet(state: TestItemState, testGroup: TestGroup, onDismiss: () -> Unit) {
@OptIn(ExperimentalMaterial3Api::class) if (state.testItem != null) {
@Composable val testItems by state.testItem.collectAsState(initial = emptyList())
fun TestDetailSheet(state: TestItemState, testGroup: TestGroup, onDismiss: () -> Unit) {
if (state.testItem != null) { CustomModalBottomSheet(
val testItems by state.testItem.collectAsState(initial = emptyList()) onDismiss = onDismiss,
content = {
CustomModalBottomSheet( CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
onDismiss = onDismiss, LazyColumn(
content = { modifier = Modifier
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { .fillMaxWidth()
LazyColumn( .padding(horizontal = MaterialTheme.dime.lg),
modifier = Modifier contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg)
.fillMaxWidth() ) {
.padding(horizontal = MaterialTheme.dime.lg), item {
contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg) Text(
) { text = testGroup.fname ?: "جزئیات آزمایش",
item { style = MaterialTheme.typography.headlineMedium,
Text( fontWeight = FontWeight.Bold,
text = testGroup.fname ?: "جزئیات آزمایش", modifier = Modifier.padding(bottom = MaterialTheme.dime.md)
style = MaterialTheme.typography.headlineMedium, )
fontWeight = FontWeight.Bold, Text(
modifier = Modifier.padding(bottom = MaterialTheme.dime.md) text = testGroup.ename ?: "N/A (English Name)",
) style = MaterialTheme.typography.titleMedium,
Text( color = MaterialTheme.colorScheme.onSurfaceVariant,
text = testGroup.ename ?: "N/A (English Name)", modifier = Modifier.padding(bottom = MaterialTheme.dime.sm)
style = MaterialTheme.typography.titleMedium, )
color = MaterialTheme.colorScheme.onSurfaceVariant, if (testGroup.detail != null){
modifier = Modifier.padding(bottom = MaterialTheme.dime.sm) Text(testGroup.detail , style = MaterialTheme.typography.bodySmall,modifier = Modifier.padding(bottom = MaterialTheme.dime.md) )
) }
if (testGroup.detail != null){
Text(testGroup.detail , style = MaterialTheme.typography.bodySmall,modifier = Modifier.padding(bottom = MaterialTheme.dime.md) )
}
HorizontalDivider(Modifier.padding(bottom = MaterialTheme.dime.md))
}
HorizontalDivider(Modifier.padding(bottom = MaterialTheme.dime.md)) if (testItems.isEmpty()) {
} item {
if (testItems.isEmpty()) { }
item { } else {
items(testItems) { testItem ->
} TestItemDetailCard(testItem = testItem)
} else { }
items(testItems) { testItem -> }
TestItemDetailCard(testItem = testItem)
}
} }
}
}
} )
} }
} }
)
} @Composable
} fun TestItemDetailCard(testItem: com.approagency.pharmacy.domain.model.TestItem) {
Column(
@Composable modifier = Modifier
fun TestItemDetailCard(testItem: com.approagency.drug.domain.model.TestItem) { .fillMaxWidth()
Column( .padding(vertical = MaterialTheme.dime.md)
modifier = Modifier ) {
.fillMaxWidth() Text(
.padding(vertical = MaterialTheme.dime.md) text = testItem.title ?: "بدون عنوان",
) { style = MaterialTheme.typography.titleMedium,
Text( fontWeight = FontWeight.SemiBold
text = testItem.title ?: "بدون عنوان", )
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold if (!testItem.normalValue.isNullOrBlank()) {
) Text(
text = "مقدار نرمال: ${testItem.normalValue}",
if (!testItem.normalValue.isNullOrBlank()) { style = MaterialTheme.typography.bodyMedium,
Text( color = MaterialTheme.colorScheme.primary,
text = "مقدار نرمال: ${testItem.normalValue}", modifier = Modifier.padding(top = MaterialTheme.dime.xs)
style = MaterialTheme.typography.bodyMedium, )
color = MaterialTheme.colorScheme.primary, }
modifier = Modifier.padding(top = MaterialTheme.dime.xs)
) if (!testItem.detail.isNullOrBlank()) {
} Text(
text = testItem.detail,
if (!testItem.detail.isNullOrBlank()) { style = MaterialTheme.typography.bodySmall,
Text( color = MaterialTheme.colorScheme.onSurfaceVariant,
text = testItem.detail, modifier = Modifier.padding(top = MaterialTheme.dime.sm)
style = MaterialTheme.typography.bodySmall, )
color = MaterialTheme.colorScheme.onSurfaceVariant, }
modifier = Modifier.padding(top = MaterialTheme.dime.sm)
) HorizontalDivider(
} modifier = Modifier.padding(top = MaterialTheme.dime.md),
thickness = 0.5.dp
HorizontalDivider( )
modifier = Modifier.padding(top = MaterialTheme.dime.md), }
thickness = 0.5.dp
)
}
} }
@@ -1,90 +1,90 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
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.automirrored.twotone.KeyboardArrowLeft
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.AbsoluteAlignment import androidx.compose.ui.AbsoluteAlignment
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import com.approagency.drug.domain.model.TestGroup import com.approagency.pharmacy.domain.model.TestGroup
import com.approagency.drug.presentation.common.CustomBox import com.approagency.pharmacy.presentation.common.CustomBox
import com.vada.caller.ui.theme.dime import com.vada.caller.ui.theme.dime
@Composable @Composable
fun TestGroupItemContent( fun TestGroupItemContent(
group: TestGroup, group: TestGroup,
onClick: () -> Unit onClick: () -> Unit
) { ) {
CustomBox( CustomBox(
modifier = Modifier modifier = Modifier
.padding(bottom = MaterialTheme.dime.sm) .padding(bottom = MaterialTheme.dime.sm)
.clickable( .clickable(
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
onClick() onClick()
}, },
) { ) {
Row( Row(
modifier = Modifier.padding(all = MaterialTheme.dime.lg), modifier = Modifier.padding(all = MaterialTheme.dime.lg),
) { ) {
Column ( Column (
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.Start horizontalAlignment = Alignment.Start
) { ) {
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalAlignment = AbsoluteAlignment.Right horizontalAlignment = AbsoluteAlignment.Right
// horizontalArrangement = Arrangement.SpaceBetween // horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Text(group.fname ?: "" , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right ) Text(group.fname ?: "" , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right )
Text(group.ename ?: "" , style = MaterialTheme.typography.bodySmall , textAlign = TextAlign.Left) Text(group.ename ?: "" , style = MaterialTheme.typography.bodySmall , textAlign = TextAlign.Left)
Spacer(modifier = Modifier.padding(vertical = MaterialTheme.dime.xs)) Spacer(modifier = Modifier.padding(vertical = MaterialTheme.dime.xs))
Text(group.detail ?: "" , style = MaterialTheme.typography.bodySmall.copy( Text(group.detail ?: "" , style = MaterialTheme.typography.bodySmall.copy(
textDirection = TextDirection.Rtl textDirection = TextDirection.Rtl
) , textAlign = TextAlign.Right , maxLines = 2 , softWrap = true , overflow = TextOverflow.Ellipsis) ) , textAlign = TextAlign.Right , maxLines = 2 , softWrap = true , overflow = TextOverflow.Ellipsis)
} }
Row ( Row (
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
TextButton( TextButton(
onClick = { onClick = {
onClick() onClick()
} }
) { ) {
Row( Row(
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
Icon( Icon(
imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
, contentDescription = "see more" , contentDescription = "see more"
) )
Text("مشاهده جزییات") Text("مشاهده جزییات")
} }
} }
} }
} }
} }
} }
} }
@@ -1,27 +1,20 @@
package com.approagency.drug.presentation.components package com.approagency.pharmacy.presentation.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement 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
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
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.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Divider import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -33,23 +26,18 @@ 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.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.approagency.drug.R import com.approagency.pharmacy.domain.model.DrugDetail
import com.approagency.drug.domain.model.DrugDetail import com.approagency.pharmacy.presentation.common.CustomModalBottomSheet
import com.approagency.drug.presentation.common.CustomModalBottomSheet import com.approagency.pharmacy.presentation.common.ErrorState
import com.approagency.drug.presentation.common.ErrorState import com.approagency.pharmacy.presentation.viewModel.DrugDetailViewModel
import com.approagency.drug.presentation.viewModel.DrugDetailViewModel import com.approagency.pharmacy.presentation.viewModel.DrugDetailYabState
import com.approagency.drug.presentation.viewModel.DrugDetailYabState
import com.vada.caller.ui.theme.LocalDime import com.vada.caller.ui.theme.LocalDime
import com.vada.caller.ui.theme.dime
import kotlinx.coroutines.delay
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@@ -421,7 +409,7 @@ fun InfoSection(
} }
@Composable @Composable
fun DosageFormsSection(dosageForms: List<com.approagency.drug.domain.model.DosageForm>) { fun DosageFormsSection(dosageForms: List<com.approagency.pharmacy.domain.model.DosageForm>) {
val dime = LocalDime.current val dime = LocalDime.current
Column { Column {
@@ -477,7 +465,7 @@ fun DosageFormsSection(dosageForms: List<com.approagency.drug.domain.model.Dosag
} }
@Composable @Composable
fun BrandNamesSection(brandNames: List<com.approagency.drug.domain.model.BrandName>) { fun BrandNamesSection(brandNames: List<com.approagency.pharmacy.domain.model.BrandName>) {
val dime = LocalDime.current val dime = LocalDime.current
Column { Column {
@@ -1,7 +1,5 @@
package com.approagency.drug.presentation.screens package com.approagency.pharmacy.presentation.screens
import android.R
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement 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
@@ -26,7 +24,6 @@ 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.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow import androidx.compose.material3.TabRow
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -50,12 +47,11 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController import androidx.navigation.NavController
import com.approagency.drug.domain.model.DrugDetail import com.approagency.pharmacy.domain.model.DrugDetail
import com.approagency.drug.presentation.common.ErrorState import com.approagency.pharmacy.presentation.common.ErrorState
import com.approagency.drug.presentation.viewModel.DrugDetailViewModel import com.approagency.pharmacy.presentation.viewModel.DrugDetailViewModel
import com.approagency.drug.presentation.viewModel.DrugDetailYabState import com.approagency.pharmacy.presentation.viewModel.DrugDetailYabState
import com.vada.caller.ui.theme.LocalDime import com.vada.caller.ui.theme.LocalDime
import com.vada.caller.ui.theme.dime
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -231,7 +227,7 @@ fun DrugDetailScreen(
@Composable @Composable
fun ManufacturerInfoCard( fun ManufacturerInfoCard(
manufacturer: String, manufacturer: String,
genericInfo: com.approagency.drug.domain.model.GenericInfo?, genericInfo: com.approagency.pharmacy.domain.model.GenericInfo?,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val dime = LocalDime.current val dime = LocalDime.current
@@ -1,251 +1,210 @@
package com.approagency.drug.presentation.screens package com.approagency.pharmacy.presentation.screens
import android.R import androidx.compose.foundation.layout.Arrangement
import android.R.attr.fontWeight import androidx.compose.foundation.layout.Box
import android.content.res.Resources import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.background import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.border import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.gestures.snapping.SnapPosition import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.Column import androidx.compose.material.icons.Icons
import androidx.compose.foundation.layout.Row import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.foundation.layout.Spacer import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Icon
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.MaterialTheme
import androidx.compose.foundation.layout.height import androidx.compose.material3.Text
import androidx.compose.foundation.layout.padding import androidx.compose.material3.TextButton
import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.width import androidx.compose.runtime.collectAsState
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.getValue
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.runtime.mutableStateOf
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.remember
import androidx.compose.foundation.text.BasicTextField import androidx.compose.runtime.setValue
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.ui.Alignment
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.Modifier
import androidx.compose.material.icons.Icons import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.material.icons.filled.Info import androidx.compose.ui.text.style.TextAlign
import androidx.compose.material.icons.filled.KeyboardArrowLeft import androidx.navigation.NavController
import androidx.compose.material.icons.filled.KeyboardArrowRight import com.approagency.pharmacy.domain.model.DrugSearchParams
import androidx.compose.material.icons.filled.Search import com.approagency.pharmacy.presentation.common.CustomModalDialog
import androidx.compose.material.icons.twotone.Info import com.approagency.pharmacy.presentation.common.Loading
import androidx.compose.material.icons.twotone.KeyboardArrowLeft import com.approagency.pharmacy.presentation.components.DarmanContent
import androidx.compose.material.icons.twotone.KeyboardArrowRight import com.approagency.pharmacy.presentation.components.DrugDetailSheet
import androidx.compose.material3.Button import com.approagency.pharmacy.presentation.components.DrugListContent
import androidx.compose.material3.ExperimentalMaterial3Api import com.approagency.pharmacy.presentation.components.EmptyStateContent
import androidx.compose.material3.Icon import com.approagency.pharmacy.presentation.viewModel.CombineState
import androidx.compose.material3.IconButton import com.approagency.pharmacy.presentation.viewModel.HomeEvent
import androidx.compose.material3.MaterialTheme import com.approagency.pharmacy.presentation.viewModel.HomeViewModel
import androidx.compose.material3.OutlinedTextField import com.vada.caller.ui.theme.dime
import androidx.compose.material3.Text import kotlinx.coroutines.flow.SharedFlow
import androidx.compose.material3.TextButton import org.koin.androidx.compose.koinViewModel
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider @OptIn(ExperimentalMaterial3Api::class)
import androidx.compose.runtime.LaunchedEffect @Composable
import androidx.compose.runtime.collectAsState fun HomeScreen(
import androidx.compose.runtime.getValue navController : NavController,
import androidx.compose.runtime.mutableStateOf modifier: Modifier,
import androidx.compose.runtime.remember viewModel: HomeViewModel = koinViewModel()
import androidx.compose.runtime.setValue ) {
import androidx.compose.ui.Alignment val state by viewModel.uiState.collectAsState()
import androidx.compose.ui.Modifier val focusManager = LocalFocusManager.current
import androidx.compose.ui.platform.LocalFocusManager val keyboardController = LocalSoftwareKeyboardController.current
import androidx.compose.ui.platform.LocalLayoutDirection if (state.drugDetailState.isDetailVisible) {
import androidx.compose.ui.platform.LocalSoftwareKeyboardController DrugDetailSheet(
import androidx.compose.ui.text.font.FontWeight viewModel = viewModel,
import androidx.compose.ui.text.style.TextAlign state = state.drugDetailState
import androidx.compose.ui.unit.LayoutDirection )
import androidx.compose.ui.unit.TextUnit }
import androidx.compose.ui.unit.dp HomeContent(
import androidx.lifecycle.viewmodel.compose.viewModel modifier = modifier,
import androidx.navigation.NavController state = state,
import com.approagency.drug.data.dto.DrugDetail viewModelEvent = viewModel.event,
import com.approagency.drug.data.dto.DrugListResponse onSearch = { value ->
import com.approagency.drug.domain.model.DrugSearchParams if (value is Int) {
import com.approagency.drug.presentation.common.CustomBox viewModel.searchDrugs(DrugSearchParams(healGroup = value))
import com.approagency.drug.presentation.common.CustomModalBottomSheet } else if (value is String) {
import com.approagency.drug.presentation.common.CustomModalDialog viewModel.searchDrugs(DrugSearchParams(query = value))
import com.approagency.drug.presentation.common.CustomTextFilled }
import com.approagency.drug.presentation.common.Loading keyboardController?.hide()
import com.approagency.drug.presentation.common.PrimaryButton focusManager.clearFocus()},
import com.approagency.drug.presentation.components.DarmanContent onRetry = {
import com.approagency.drug.presentation.components.DrugDetailSheet viewModel.getDarmani()
import com.approagency.drug.presentation.components.DrugListContent },
import com.approagency.drug.presentation.components.EmptyStateContent onDrugClick = { drugId -> viewModel.drugDetail(drugId , allDrugs = state.drugSearchState.drugsData) }
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 @OptIn(ExperimentalMaterial3Api::class)
import com.vada.caller.ui.theme.dime @Composable
import kotlinx.coroutines.flow.SharedFlow fun HomeContent(
import org.koin.androidx.compose.koinViewModel state: CombineState,
viewModelEvent: SharedFlow<HomeEvent>,
onSearch: (Any) -> Unit,
@OptIn(ExperimentalMaterial3Api::class) onRetry: () -> Unit,
@Composable onDrugClick: (Int) -> Unit,
fun HomeScreen( modifier: Modifier = Modifier
navController : NavController, ) {
modifier: Modifier, var searchText by remember { mutableStateOf("") }
viewModel: HomeViewModel = koinViewModel() Column(
) { modifier = modifier
val state by viewModel.uiState.collectAsState() .fillMaxSize().padding(horizontal = MaterialTheme.dime.md)
val focusManager = LocalFocusManager.current .padding(top = MaterialTheme.dime.md),
val keyboardController = LocalSoftwareKeyboardController.current horizontalAlignment = Alignment.Start,
if (state.drugDetailState.isDetailVisible) { ) {
DrugDetailSheet( Column (
viewModel = viewModel, verticalArrangement = Arrangement.Center
state = state.drugDetailState ){
) // CustomTextFilled(
} // value = searchText,
HomeContent( // onValueChange = { searchText = it },
modifier = modifier, // onSearch = { query ->
state = state, // if (query.isNotBlank()) {
viewModelEvent = viewModel.event, // onSearch(query)
onSearch = { value -> // }
if (value is Int) { // },
viewModel.searchDrugs(DrugSearchParams(healGroup = value)) // placeholder = "جستجوی دارو",
} else if (value is String) { // showClearButton = true,
viewModel.searchDrugs(DrugSearchParams(query = value)) // showSearchButton = true,
} // autoSearch = false, // Set to true if you want search while typing
keyboardController?.hide() // height = 45
focusManager.clearFocus()}, // )
onRetry = { // Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
viewModel.getDarmani() // PrimaryButton(
}, // text = "جستجو",
onDrugClick = { drugId -> viewModel.drugDetail(drugId , allDrugs = state.drugSearchState.drugsData) } // height = 40,
) // isLoading = state.drugSearchState.isLoading,
} // onClick = {
// if (searchText.isNotBlank()) {
@OptIn(ExperimentalMaterial3Api::class) // onSearch(searchText)
@Composable // }
fun HomeContent( // }
state: CombineState, // )
viewModelEvent: SharedFlow<HomeEvent>, // Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
onSearch: (Any) -> Unit, Box(modifier = Modifier.fillMaxSize()) {
onRetry: () -> Unit,
onDrugClick: (Int) -> Unit, // Always draw the data if available
modifier: Modifier = Modifier if (state.drugSearchState.drugsData != null && !state.showDarmanList) {
) { Column {
var searchText by remember { mutableStateOf("") } Row(
Column( horizontalArrangement = Arrangement.End,
modifier = modifier modifier = Modifier.fillMaxWidth()
.fillMaxSize().padding(horizontal = MaterialTheme.dime.md) ) {
.padding(top = MaterialTheme.dime.md), TextButton(
horizontalAlignment = Alignment.Start, onClick = {
) { onRetry()
Column ( }
verticalArrangement = Arrangement.Center ) {
){ Text("مشاهده تمامی گروه های دارویی")
// CustomTextFilled( Icon(
// value = searchText, imageVector = Icons.Default.KeyboardArrowRight,
// onValueChange = { searchText = it }, contentDescription = "back"
// onSearch = { query -> )
// if (query.isNotBlank()) { Spacer(modifier = Modifier.width(MaterialTheme.dime.sm))
// onSearch(query) }
// } }
// }, DrugListContent(
// placeholder = "جستجوی دارو", drugsData = state.drugSearchState.drugsData,
// showClearButton = true, onDrugClick = onDrugClick
// showSearchButton = true, )
// autoSearch = false, // Set to true if you want search while typing }
// height = 45 }
// ) else if (state.darmanState.getDarmani != null ){
// Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) Column {
// PrimaryButton(
// text = "جستجو", Row(
// height = 40, modifier = Modifier.fillMaxWidth(),
// isLoading = state.drugSearchState.isLoading, horizontalArrangement = Arrangement.End
// onClick = { ) {
// if (searchText.isNotBlank()) { Text("لیست گروه های دارویی" , style = MaterialTheme.typography.labelLarge.copy(
// onSearch(searchText) color = MaterialTheme.colorScheme.primary
// } ) ,modifier= Modifier.padding(MaterialTheme.dime.md))
// } }
// )
// Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) DarmanContent(
Box(modifier = Modifier.fillMaxSize()) { darmanData = state.darmanState.getDarmani,
onDarmanClick = { value ->
// Always draw the data if available onSearch(value)
if (state.drugSearchState.drugsData != null && !state.showDarmanList) { },
Column { onRetryClick = {
Row( onRetry()
horizontalArrangement = Arrangement.End, }
modifier = Modifier.fillMaxWidth() )
) { }
TextButton( } else {
onClick = { EmptyStateContent()
onRetry() }
}
) {
Text("مشاهده تمامی گروه های دارویی")
Icon( // Draw the loading dialog on top if needed
imageVector = Icons.Default.KeyboardArrowRight, if (state.drugSearchState.isLoading || state.drugDetailState.isLoading || state.darmanState.isLoading) {
contentDescription = "back" CustomModalDialog(
) onDismissRequest = { /* maybe disable dismiss while loading */ },
Spacer(modifier = Modifier.width(MaterialTheme.dime.sm)) content = {
} Column(horizontalAlignment = Alignment.CenterHorizontally) {
} Loading(color = MaterialTheme.colorScheme.primary)
DrugListContent( Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
drugsData = state.drugSearchState.drugsData, Text("در حال جستجو..." , textAlign = TextAlign.Right)
onDrugClick = onDrugClick }
) },
} // modifier = Modifier
} // .matchParentSize()
else if (state.darmanState.getDarmani != null ){ // .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
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()
}
// 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)
}
},
// modifier = Modifier
// .matchParentSize()
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
)
}
}
}
}
}
@@ -1,207 +1,190 @@
package com.approagency.drug.presentation.screens package com.approagency.pharmacy.presentation.screens
import android.R.attr.onClick import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.Text
import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.padding import androidx.compose.runtime.collectAsState
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.getValue
import androidx.compose.foundation.lazy.items import androidx.compose.runtime.mutableStateOf
import androidx.compose.material.icons.Icons import androidx.compose.runtime.remember
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft import androidx.compose.runtime.setValue
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.ui.Alignment
import androidx.compose.material3.Icon import androidx.compose.ui.Modifier
import androidx.compose.material3.MaterialTheme import androidx.compose.ui.text.style.TextAlign
import androidx.compose.material3.Text import com.approagency.pharmacy.domain.model.TestGroup
import androidx.compose.material3.TextButton import com.approagency.pharmacy.domain.model.TestItem
import androidx.compose.runtime.Composable import com.approagency.pharmacy.presentation.common.CustomModalDialog
import androidx.compose.runtime.collectAsState import com.approagency.pharmacy.presentation.common.CustomTextFilled
import androidx.compose.runtime.getValue import com.approagency.pharmacy.presentation.common.Loading
import androidx.compose.runtime.mutableStateOf import com.approagency.pharmacy.presentation.common.PrimaryButton
import androidx.compose.runtime.remember import com.approagency.pharmacy.presentation.components.RetryContent
import androidx.compose.runtime.setValue import com.approagency.pharmacy.presentation.components.SearchResultsContent
import androidx.compose.ui.AbsoluteAlignment import com.approagency.pharmacy.presentation.components.TestDetailSheet
import androidx.compose.ui.Alignment import com.approagency.pharmacy.presentation.components.TestGroupItemContent
import androidx.compose.ui.Modifier import com.approagency.pharmacy.presentation.viewModel.LabViewModel
import androidx.compose.ui.text.style.TextAlign import com.vada.caller.ui.theme.dime
import androidx.compose.ui.text.style.TextDirection import kotlinx.coroutines.flow.Flow
import androidx.compose.ui.text.style.TextOverflow import org.koin.androidx.compose.koinViewModel
import androidx.compose.ui.unit.dp
import com.approagency.drug.domain.model.TestGroup @Composable
import com.approagency.drug.domain.model.TestItem fun LabScreen(
import com.approagency.drug.presentation.common.CustomBox modifier: Modifier = Modifier,
import com.approagency.drug.presentation.common.CustomModalDialog viewModel: LabViewModel = koinViewModel()
import com.approagency.drug.presentation.common.CustomTextFilled ) {
import com.approagency.drug.presentation.common.Loading val testGroupsState by viewModel.testGroups.collectAsState()
import com.approagency.drug.presentation.common.PrimaryButton val testItemsState by viewModel.testItems.collectAsState()
import com.approagency.drug.presentation.components.RetryContent val searchResultsState by viewModel.searchResults.collectAsState()
import com.approagency.drug.presentation.components.SearchResultsContent
import com.approagency.drug.presentation.components.TestDetailSheet val isLoading = testGroupsState.isLoading || testItemsState.isLoading || searchResultsState.isLoading
import com.approagency.drug.presentation.components.TestGroupItemContent val error = testGroupsState.error ?: searchResultsState.error
import com.approagency.drug.presentation.viewModel.LabViewModel val testGroups = testGroupsState.testGroup
import com.vada.caller.ui.theme.dime
import kotlinx.coroutines.flow.Flow var selectedGroup by remember { mutableStateOf<TestGroup?>(null) }
import kotlinx.coroutines.flow.count var selectedItem by remember { mutableStateOf<TestItem?>(null) }
import org.koin.androidx.compose.koinViewModel var showSheet by remember { mutableStateOf(false) }
var searchText by remember { mutableStateOf("") }
@Composable Column(modifier = modifier.fillMaxSize().padding(horizontal = MaterialTheme.dime.md)
fun LabScreen( .padding(top = MaterialTheme.dime.md),) {
modifier: Modifier = Modifier, CustomTextFilled(
viewModel: LabViewModel = koinViewModel() value = searchText,
) { onValueChange = {
val testGroupsState by viewModel.testGroups.collectAsState() searchText = it
val testItemsState by viewModel.testItems.collectAsState() viewModel.updateSearchQuery(it)
val searchResultsState by viewModel.searchResults.collectAsState() },
onSearch = { query ->
val isLoading = testGroupsState.isLoading || testItemsState.isLoading || searchResultsState.isLoading if (query.isNotBlank()) {
val error = testGroupsState.error ?: searchResultsState.error viewModel.updateSearchQuery(query)
val testGroups = testGroupsState.testGroup }
},
var selectedGroup by remember { mutableStateOf<TestGroup?>(null) } placeholder = "جستجو در گروه‌ها و آیتم‌های آزمایشگاهی",
var selectedItem by remember { mutableStateOf<TestItem?>(null) } showClearButton = true,
var showSheet by remember { mutableStateOf(false) } showSearchButton = true,
var searchText by remember { mutableStateOf("") } autoSearch = true,
Column(modifier = modifier.fillMaxSize().padding(horizontal = MaterialTheme.dime.md) height = 45
.padding(top = MaterialTheme.dime.md),) { )
CustomTextFilled( Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
value = searchText, PrimaryButton(
onValueChange = { text = "جستجو",
searchText = it height = 40,
viewModel.updateSearchQuery(it) isLoading =false,
}, onClick = {}
onSearch = { query -> )
if (query.isNotBlank()) { Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
viewModel.updateSearchQuery(query) if (searchResultsState.query.isNotBlank()) {
} // Search Results Section
}, SearchResultsContent(
placeholder = "جستجو در گروه‌ها و آیتم‌های آزمایشگاهی", searchResult = searchResultsState.searchResults,
showClearButton = true, isLoading = searchResultsState.isLoading,
showSearchButton = true, error = searchResultsState.error,
autoSearch = true, onGroupClick = { group ->
height = 45 selectedGroup = group
) viewModel.getItemByGroupId(group.id)
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) showSheet = true
PrimaryButton( },
text = "جستجو", onItemClick = { item ->
height = 40, selectedItem = item
isLoading =false, // You can show item detail sheet here
onClick = {} // For now, we'll show the parent group
) viewModel.getItemByGroupId(item.groupId)
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) showSheet = true
if (searchResultsState.query.isNotBlank()) { },
// Search Results Section onClearSearch = {
SearchResultsContent( searchText = ""
searchResult = searchResultsState.searchResults, viewModel.clearSearch()
isLoading = searchResultsState.isLoading, viewModel.loadTestGroups()
error = searchResultsState.error, }
onGroupClick = { group -> )
selectedGroup = group } else if(error != null){
viewModel.getItemByGroupId(group.id) RetryContent(modifier = modifier , error = error , onClick = {
showSheet = true viewModel.loadTestGroups()
}, })
onItemClick = { item -> }
selectedItem = item else if (testGroups != null){
// You can show item detail sheet here TestGroupsList(
// For now, we'll show the parent group testGroupsFlow = testGroups,
viewModel.getItemByGroupId(item.groupId) viewModel = viewModel,
showSheet = true onGroupClick = { group ->
}, selectedGroup = group
onClearSearch = { viewModel.getItemByGroupId(group.id)
searchText = "" showSheet = true
viewModel.clearSearch() }
viewModel.loadTestGroups() )
} }
) else {
} else if(error != null){ Box(
RetryContent(modifier = modifier , error = error , onClick = { modifier = Modifier.fillMaxSize(),
viewModel.loadTestGroups() contentAlignment = Alignment.Center
}) ) {
} Text("هیچ داده‌ای موجود نیست")
else if (testGroups != null){ }
TestGroupsList( }
testGroupsFlow = testGroups,
viewModel = viewModel, }
onGroupClick = { group -> if (isLoading) {
selectedGroup = group CustomModalDialog(
viewModel.getItemByGroupId(group.id) onDismissRequest = { /* maybe disable dismiss while loading */ },
showSheet = true content = {
} Column(horizontalAlignment = Alignment.CenterHorizontally) {
) Loading(color = MaterialTheme.colorScheme.primary)
} Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
else { Text("در حال جستجو..." , textAlign = TextAlign.Right)
Box( }
modifier = Modifier.fillMaxSize(), },
contentAlignment = Alignment.Center // modifier = Modifier
) { // .matchParentSize()
Text("هیچ داده‌ای موجود نیست") // .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
} )
} }
if (showSheet && selectedGroup != null) {
} TestDetailSheet(
if (isLoading) { state = testItemsState,
CustomModalDialog( testGroup = selectedGroup!!,
onDismissRequest = { /* maybe disable dismiss while loading */ }, onDismiss = { showSheet = false }
content = { )
Column(horizontalAlignment = Alignment.CenterHorizontally) { }
Loading(color = MaterialTheme.colorScheme.primary) }
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
Text("در حال جستجو..." , textAlign = TextAlign.Right) @Composable
} fun TestGroupsList(
}, testGroupsFlow: Flow<List<TestGroup>>,
// modifier = Modifier viewModel: LabViewModel,
// .matchParentSize() onGroupClick: (TestGroup) -> Unit
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f)) ) {
) val testGroups by testGroupsFlow.collectAsState(initial = emptyList())
}
if (showSheet && selectedGroup != null) { if (testGroups.isEmpty()) {
TestDetailSheet( CustomModalDialog(
state = testItemsState, onDismissRequest = { /* maybe disable dismiss while loading */ },
testGroup = selectedGroup!!, content = {
onDismiss = { showSheet = false } Column(horizontalAlignment = Alignment.CenterHorizontally) {
) Loading(color = MaterialTheme.colorScheme.primary)
} Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
} Text("در حال جستجو..." , textAlign = TextAlign.Right)
}
@Composable },
fun TestGroupsList( // modifier = Modifier
testGroupsFlow: Flow<List<TestGroup>>, // .matchParentSize()
viewModel: LabViewModel, // .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
onGroupClick: (TestGroup) -> Unit )
) { } else {
val testGroups by testGroupsFlow.collectAsState(initial = emptyList()) LazyColumn(
modifier = Modifier.fillMaxSize()
if (testGroups.isEmpty()) { .fillMaxSize().padding( vertical =MaterialTheme.dime.md )
CustomModalDialog( ) {
onDismissRequest = { /* maybe disable dismiss while loading */ }, items(testGroups) { group ->
content = { TestGroupItemContent(
Column(horizontalAlignment = Alignment.CenterHorizontally) { group = group,
Loading(color = MaterialTheme.colorScheme.primary) onClick = { onGroupClick(group) }
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm)) )
Text("در حال جستجو..." , textAlign = TextAlign.Right) }
} }
}, }
// modifier = Modifier }
// .matchParentSize()
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
)
} else {
LazyColumn(
modifier = Modifier.fillMaxSize()
.fillMaxSize().padding( vertical =MaterialTheme.dime.md )
) {
items(testGroups) { group ->
TestGroupItemContent(
group = group,
onClick = { onGroupClick(group) }
)
}
}
}
}
@@ -1,124 +1,124 @@
package com.approagency.drug.presentation.screens package com.approagency.pharmacy.presentation.screens
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.consumeWindowInsets
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.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.rounded.Refresh import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults import androidx.compose.material3.ScaffoldDefaults
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.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
@SuppressLint("ConfigurationScreenWidthHeight") @SuppressLint("ConfigurationScreenWidthHeight")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun RootScreen(navHostController: NavHostController, modifier: Modifier){ fun RootScreen(navHostController: NavHostController, modifier: Modifier){
val pages = listOf( val pages = listOf(
"دارو" to Icons.Default.Search, "دارو" to Icons.Default.Search,
"آزمایش" to Icons.Default.Build, "آزمایش" to Icons.Default.Build,
"سرچ" to Icons.Rounded.Refresh "سرچ" to Icons.Rounded.Refresh
) )
var seletedTab by remember { mutableStateOf(0) } var seletedTab by remember { mutableStateOf(0) }
val context = LocalContext.current val context = LocalContext.current
val activity = context as Activity val activity = context as Activity
Scaffold ( Scaffold (
contentWindowInsets = ScaffoldDefaults.contentWindowInsets, contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
topBar = { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl){ topBar = { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl){
TopAppBar( TopAppBar(
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer, containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary, titleContentColor = MaterialTheme.colorScheme.primary,
), ),
title = { title = {
Text("دستیار سلامت" , textAlign = TextAlign.Right , style = MaterialTheme.typography.titleLarge , fontWeight = FontWeight.W700 ) Text("دستیار سلامت" , textAlign = TextAlign.Right , style = MaterialTheme.typography.titleLarge , fontWeight = FontWeight.W700 )
} }
) )
}}, }},
bottomBar = { bottomBar = {
NavigationBar( NavigationBar(
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(110.dp).padding(bottom = 2.dp).clip(MaterialTheme.shapes.extraLarge), .height(110.dp).padding(bottom = 2.dp).clip(MaterialTheme.shapes.extraLarge),
tonalElevation = 8.dp, // subtle shadow tonalElevation = 8.dp, // subtle shadow
windowInsets = WindowInsets.navigationBars windowInsets = WindowInsets.navigationBars
.only(WindowInsetsSides.Bottom) .only(WindowInsetsSides.Bottom)
) { ) {
pages.forEachIndexed { index, item -> pages.forEachIndexed { index, item ->
val selected = seletedTab == index val selected = seletedTab == index
NavigationBarItem( NavigationBarItem(
colors = NavigationBarItemDefaults.colors( colors = NavigationBarItemDefaults.colors(
selectedIconColor = MaterialTheme.colorScheme.onPrimary, selectedIconColor = MaterialTheme.colorScheme.onPrimary,
selectedTextColor = MaterialTheme.colorScheme.primary, selectedTextColor = MaterialTheme.colorScheme.primary,
indicatorColor = MaterialTheme.colorScheme.primary, indicatorColor = MaterialTheme.colorScheme.primary,
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant, unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
), ),
selected = selected, selected = selected,
onClick = { seletedTab = index }, onClick = { seletedTab = index },
icon = { icon = {
Icon( Icon(
imageVector = item.second, imageVector = item.second,
contentDescription = item.first, contentDescription = item.first,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
}, },
label = { label = {
Text( Text(
item.first, item.first,
style = MaterialTheme.typography.labelMedium style = MaterialTheme.typography.labelMedium
) )
}, },
alwaysShowLabel = true alwaysShowLabel = true
) )
} }
} }
} }
) { ) {
padding -> when (seletedTab) { padding -> when (seletedTab) {
0 -> HomeScreen( navController = navHostController,Modifier.padding(padding).consumeWindowInsets(padding)) 0 -> HomeScreen( navController = navHostController,Modifier.padding(padding).consumeWindowInsets(padding))
1 -> LabScreen(Modifier.padding(padding)) 1 -> LabScreen(Modifier.padding(padding))
2 -> SearchScreen( 2 -> SearchScreen(
navHostController, navHostController,
modifier = Modifier.padding(padding).consumeWindowInsets(padding), modifier = Modifier.padding(padding).consumeWindowInsets(padding),
) )
} }
} }
} }
@@ -1,4 +1,4 @@
package com.approagency.drug.presentation.screens package com.approagency.pharmacy.presentation.screens
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -24,19 +24,19 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.navigation.NavController import androidx.navigation.NavController
import com.approagency.drug.domain.model.DrugSearchResult import com.approagency.pharmacy.domain.model.DrugSearchResult
import com.approagency.drug.navigation.Screen import com.approagency.pharmacy.navigation.Screen
import com.approagency.drug.presentation.common.CustomTextFilled import com.approagency.pharmacy.presentation.common.CustomTextFilled
import com.approagency.drug.presentation.common.EmptySearchState import com.approagency.pharmacy.presentation.common.EmptySearchState
import com.approagency.drug.presentation.common.EndOfListIndicator import com.approagency.pharmacy.presentation.common.EndOfListIndicator
import com.approagency.drug.presentation.common.ErrorState import com.approagency.pharmacy.presentation.common.ErrorState
import com.approagency.drug.presentation.common.LoadingMoreIndicator import com.approagency.pharmacy.presentation.common.LoadingMoreIndicator
import com.approagency.drug.presentation.common.PrimaryButton import com.approagency.pharmacy.presentation.common.PrimaryButton
import com.approagency.drug.presentation.components.DaroYabSearchResult import com.approagency.pharmacy.presentation.components.DaroYabSearchResult
import com.approagency.drug.presentation.components.DrugDetailBottomSheet import com.approagency.pharmacy.presentation.components.DrugDetailBottomSheet
import com.approagency.drug.presentation.components.PharmacyBottomSheet import com.approagency.pharmacy.presentation.components.PharmacyBottomSheet
import com.approagency.drug.presentation.viewModel.DrugDetailViewModel import com.approagency.pharmacy.presentation.viewModel.DrugDetailViewModel
import com.approagency.drug.presentation.viewModel.PharmacyViewModel import com.approagency.pharmacy.presentation.viewModel.PharmacyViewModel
import com.approgency.drug.presentation.viewModel.SearchState import com.approgency.drug.presentation.viewModel.SearchState
import com.approgency.drug.presentation.viewModel.SearchViewModel import com.approgency.drug.presentation.viewModel.SearchViewModel
import com.vada.caller.ui.theme.LocalDime import com.vada.caller.ui.theme.LocalDime
@@ -1,9 +1,9 @@
package com.approagency.drug.presentation.viewModel package com.approagency.pharmacy.presentation.viewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.approagency.drug.domain.model.DrugDetail import com.approagency.pharmacy.domain.model.DrugDetail
import com.approagency.drug.domain.usecase.DrugDetailYabUseCase import com.approagency.pharmacy.domain.usecase.DrugDetailYabUseCase
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -1,174 +1,170 @@
package com.approagency.drug.presentation.viewModel package com.approagency.pharmacy.presentation.viewModel
import android.net.http.HttpException import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewModelScope import com.approagency.pharmacy.data.dto.DarmanModel
import com.approagency.drug.data.dto.DarmanModel import com.approagency.pharmacy.data.dto.DrugListResponse
import com.approagency.drug.data.dto.DrugDetail import com.approagency.pharmacy.data.dto.DrugModels
import com.approagency.drug.data.dto.DrugListResponse import com.approagency.pharmacy.domain.model.DrugSearchParams
import com.approagency.drug.data.dto.DrugModels import com.approagency.pharmacy.domain.usecase.GetDarmanUseCase
import com.approagency.drug.domain.model.DrugSearchParams import com.approagency.pharmacy.domain.usecase.GetDrugDetailUseCase
import com.approagency.drug.domain.usecase.GetDarmanUseCase import com.approagency.pharmacy.domain.usecase.GetDrugSearchUseCase
import com.approagency.drug.domain.usecase.GetDrugDetailUseCase import kotlinx.coroutines.flow.MutableSharedFlow
import com.approagency.drug.domain.usecase.GetDrugSearchUseCase import kotlinx.coroutines.flow.MutableStateFlow
import com.approagency.drug.presentation.screens.HomeScreen import kotlinx.coroutines.flow.StateFlow
import com.approagency.drug.utils.retryWithBackoff import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.update
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow class HomeViewModel (
import kotlinx.coroutines.flow.update private val getDrugSearchUseCase: GetDrugSearchUseCase,
import kotlinx.coroutines.launch private val getDrugDetailUseCase: GetDrugDetailUseCase,
private val getDarmanUseCase: GetDarmanUseCase
class HomeViewModel ( ): ViewModel() {
private val getDrugSearchUseCase: GetDrugSearchUseCase, private val _uiState = MutableStateFlow(CombineState())
private val getDrugDetailUseCase: GetDrugDetailUseCase, val uiState : StateFlow<CombineState> = _uiState
private val getDarmanUseCase: GetDarmanUseCase
): ViewModel() { // Add SharedFlow for one-time events
private val _uiState = MutableStateFlow(CombineState()) private val _event = MutableSharedFlow<HomeEvent>()
val uiState : StateFlow<CombineState> = _uiState val event = _event.asSharedFlow()
init {
// Add SharedFlow for one-time events getDarmani()
private val _event = MutableSharedFlow<HomeEvent>() }
val event = _event.asSharedFlow() fun getDarmani() {
init { _uiState.update {
getDarmani() it.copy(
} darmanState = it.darmanState.copy(isLoading = true),
fun getDarmani() { showDarmanList = true
_uiState.update { )
it.copy( }
darmanState = it.darmanState.copy(isLoading = true), viewModelScope.launch {
showDarmanList = true try {
) val result = getDarmanUseCase.invoke()
} _uiState.update {
viewModelScope.launch { it.copy(
try { darmanState = it.darmanState.copy(
val result = getDarmanUseCase.invoke() isLoading = false,
_uiState.update { getDarmani = result
it.copy( )
darmanState = it.darmanState.copy( )
isLoading = false, }
getDarmani = result }catch (e: Exception) {
) handleError(e.message)
) }catch (e: Exception) {
} handleError(e.localizedMessage ?: "Unknown error")
}catch (e: Exception) { }
handleError(e.message) }
}catch (e: Exception) { }
handleError(e.localizedMessage ?: "Unknown error")
} fun searchDrugs(drugSearchParams: DrugSearchParams) {
} _uiState.update { it.copy(drugSearchState = it.drugSearchState.copy(
} isLoading = true
) , showDarmanList = false) }
fun searchDrugs(drugSearchParams: DrugSearchParams) { viewModelScope.launch {
_uiState.update { it.copy(drugSearchState = it.drugSearchState.copy( try {
isLoading = true val result = getDrugSearchUseCase.invoke(drugSearchParams)
) , showDarmanList = false) } println(result)
viewModelScope.launch { _uiState.update { it.copy(
try { drugSearchState = it.drugSearchState.copy(
val result = getDrugSearchUseCase.invoke(drugSearchParams) isLoading = false,
println(result) drugsData = result
_uiState.update { it.copy( )
drugSearchState = it.drugSearchState.copy(
isLoading = false, ) }
drugsData = result } catch (e: Exception) {
) println(e.message)
handleError(e.message)
) } }catch (e: Exception) {
} catch (e: Exception) { println(e.message)
println(e.message) handleError(e.localizedMessage ?: "Unknown error")
handleError(e.message) }
}catch (e: Exception) { }
println(e.message) }
handleError(e.localizedMessage ?: "Unknown error")
} fun drugDetail(cod: Int , allDrugs:Result<DrugListResponse?>?) {
} _uiState.update { it.copy(drugDetailState = it.drugDetailState.copy(
} isLoading = true
) , showDarmanList = false) }
fun drugDetail(cod: Int , allDrugs:Result<DrugListResponse?>?) { viewModelScope.launch {
_uiState.update { it.copy(drugDetailState = it.drugDetailState.copy( try {
isLoading = true val result = getDrugDetailUseCase.invoke(cod)
) , showDarmanList = false) } _uiState.update {
viewModelScope.launch { it.copy(
try { drugDetailState = it.drugDetailState.copy(
val result = getDrugDetailUseCase.invoke(cod) isLoading = false,
_uiState.update { drugDetail = result,
it.copy( isDetailVisible = true
drugDetailState = it.drugDetailState.copy( ),
isLoading = false, drugSearchState = it.drugSearchState.copy(
drugDetail = result, isLoading = false,
isDetailVisible = true drugsData = allDrugs
), )
drugSearchState = it.drugSearchState.copy( )
isLoading = false, }
drugsData = allDrugs println(result)
) } catch (e: Exception) {
) handleError(e.message)
} }catch (e: Exception) {
println(result) handleError(e.localizedMessage ?: "Unknown error")
} catch (e: Exception) { }
handleError(e.message) }
}catch (e: Exception) { }
handleError(e.localizedMessage ?: "Unknown error") fun closeDetail() {
} _uiState.update {
} it.copy(
} drugDetailState = it.drugDetailState.copy(
fun closeDetail() { isDetailVisible = false,
_uiState.update { drugDetail = null // optional, depends if you want to keep last detail cached
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(drugSearchState = it.drugSearchState.copy(
} isLoading = false
) , drugDetailState = it.drugDetailState.copy(
private suspend fun handleError(message: String?) { isLoading = false
_uiState.update { it.copy(drugSearchState = it.drugSearchState.copy( )) }
isLoading = false _event.emit(HomeEvent.ShowError(message ?: "Error occurred"))
) , drugDetailState = it.drugDetailState.copy( }
isLoading = false
)) } // Clear error when consumed
_event.emit(HomeEvent.ShowError(message ?: "Error occurred")) fun clearError() {
} // _uiState.update { it.copy(error = null) }
}
// Clear error when consumed }
fun clearError() {
// _uiState.update { it.copy(error = null) }
} sealed class HomeEvent {
} data class ShowError(val message: String) : HomeEvent()
data class ShowDetailDrug(val drugDetail:Result<DrugModels?>?): HomeEvent()
}
sealed class HomeEvent {
data class ShowError(val message: String) : HomeEvent() data class CombineState(
data class ShowDetailDrug(val drugDetail:Result<DrugModels?>?): HomeEvent() val drugSearchState: DrugSearchState = DrugSearchState() ,
} val drugDetailState: DrugDetailState =DrugDetailState(),
val darmanState: GetDarmaniState = GetDarmaniState(),
data class CombineState( val showDarmanList: Boolean = true
val drugSearchState: DrugSearchState = DrugSearchState() , )
val drugDetailState: DrugDetailState =DrugDetailState(),
val darmanState: GetDarmaniState = GetDarmaniState(), data class DrugSearchState(
val showDarmanList: Boolean = true val isLoading: Boolean = false,
) val drugsData: Result<DrugListResponse?>? = null,
val drugDetail: Result<DrugModels?>? = null,
data class DrugSearchState( val error:String? = null
val isLoading: Boolean = false, )
val drugsData: Result<DrugListResponse?>? = null,
val drugDetail: Result<DrugModels?>? = null, data class DrugDetailState(
val error:String? = null val isLoading: Boolean = false,
) val drugDetail:Result<DrugModels?>? = null,
val isDetailVisible: Boolean = false,
data class DrugDetailState( val error:String? = null
val isLoading: Boolean = false, )
val drugDetail:Result<DrugModels?>? = null,
val isDetailVisible: Boolean = false, data class GetDarmaniState(
val error:String? = null val isLoading: Boolean = false,
) val getDarmani:Result<DarmanModel?>? = null,
val error:String? = null
data class GetDarmaniState( )
val isLoading: Boolean = false,
val getDarmani:Result<DarmanModel?>? = null,
val error:String? = null
)
@@ -1,174 +1,172 @@
package com.approagency.drug.presentation.viewModel package com.approagency.pharmacy.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.DrugListResponse import com.approagency.pharmacy.domain.model.TestGroup
import com.approagency.drug.data.dto.DrugModels import com.approagency.pharmacy.domain.model.TestItem
import com.approagency.drug.domain.model.TestGroup import com.approagency.pharmacy.domain.usecase.GetTestGroupUseCase
import com.approagency.drug.domain.model.TestItem import com.approagency.pharmacy.domain.usecase.GetTestItemByGroupId
import com.approagency.drug.domain.usecase.GetTestGroupUseCase import com.approagency.pharmacy.domain.usecase.SearchTestsUseCase
import com.approagency.drug.domain.usecase.GetTestItemByGroupId import kotlinx.coroutines.flow.Flow
import com.approagency.drug.domain.usecase.SearchTestsUseCase import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.update
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch class LabViewModel (
private val getTestGroupUseCase: GetTestGroupUseCase,
class LabViewModel ( private val getTestItemByGroupId: GetTestItemByGroupId,
private val getTestGroupUseCase: GetTestGroupUseCase, private val searchTestsUseCase: SearchTestsUseCase
private val getTestItemByGroupId: GetTestItemByGroupId, ) : ViewModel(){
private val searchTestsUseCase: SearchTestsUseCase private val _testGroups = MutableStateFlow(TestGroupItem())
) : ViewModel(){ val testGroups: StateFlow<TestGroupItem> = _testGroups.asStateFlow()
private val _testGroups = MutableStateFlow(TestGroupItem())
val testGroups: StateFlow<TestGroupItem> = _testGroups.asStateFlow() private val _testItems = MutableStateFlow(TestItemState())
val testItems: StateFlow<TestItemState> = _testItems.asStateFlow()
private val _testItems = MutableStateFlow(TestItemState())
val testItems: StateFlow<TestItemState> = _testItems.asStateFlow()
private val _searchResults = MutableStateFlow(SearchResultState())
val searchResults: StateFlow<SearchResultState> = _searchResults.asStateFlow()
private val _searchResults = MutableStateFlow(SearchResultState())
val searchResults: StateFlow<SearchResultState> = _searchResults.asStateFlow() private val searchQuery = MutableStateFlow("")
private val searchQuery = MutableStateFlow("") init {
loadTestGroups()
init { setupSearch()
loadTestGroups() }
setupSearch() private fun setupSearch() {
} viewModelScope.launch {
private fun setupSearch() { searchQuery
viewModelScope.launch { .debounce(1000) // Wait 500ms after user stops typing
searchQuery .distinctUntilChanged()
.debounce(1000) // Wait 500ms after user stops typing .collect { query ->
.distinctUntilChanged() if (query.isNotBlank()) {
.collect { query -> performSearch(query)
if (query.isNotBlank()) { } else {
performSearch(query) _searchResults.update {
} else { SearchResultState(
_searchResults.update { isLoading = false,
SearchResultState( searchResults = null,
isLoading = false, query = ""
searchResults = null, )
query = "" }
) }
} }
} }
} }
}
} fun updateSearchQuery(query: String) {
searchQuery.value = query
fun updateSearchQuery(query: String) { _searchResults.update {
searchQuery.value = query it.copy(
_searchResults.update { isLoading = query.isNotBlank(),
it.copy( query = query
isLoading = query.isNotBlank(), )
query = query }
) }
}
} private fun performSearch(query: String) {
viewModelScope.launch {
private fun performSearch(query: String) { try {
viewModelScope.launch { val result = searchTestsUseCase.invoke(query)
try { result.collect { (groups, items) ->
val result = searchTestsUseCase.invoke(query) _searchResults.update {
result.collect { (groups, items) -> it.copy(
_searchResults.update { isLoading = false,
it.copy( searchResults = SearchResult(groups = groups, items = items),
isLoading = false, error = null
searchResults = SearchResult(groups = groups, items = items), )
error = null }
) }
} } catch (e: Exception) {
} _searchResults.update {
} catch (e: Exception) { it.copy(
_searchResults.update { isLoading = false,
it.copy( searchResults = null,
isLoading = false, error = e.message ?: "خطا در جستجو"
searchResults = null, )
error = e.message ?: "خطا در جستجو" }
) }
} }
} }
} fun loadTestGroups(){
} _testGroups.update {
fun loadTestGroups(){ it.copy(
_testGroups.update { isLoading = true
it.copy( )
isLoading = true }
) viewModelScope.launch {
} try {
viewModelScope.launch { val result = getTestGroupUseCase.invoke();
try { _testGroups.update {
val result = getTestGroupUseCase.invoke(); it.copy(
_testGroups.update { isLoading = false,
it.copy( testGroup = result,
isLoading = false, )
testGroup = result, }
) } catch (e:HttpException){
} handleError(e.message)
} catch (e:HttpException){ }
handleError(e.message)
} }
}
} fun clearSearch() {
} searchQuery.value = ""
fun clearSearch() { _searchResults.update {
searchQuery.value = "" SearchResultState()
_searchResults.update { }
SearchResultState() }
} fun getItemByGroupId(id:Int){
} _testItems.update { it.copy(isLoading = true) }
fun getItemByGroupId(id:Int){ viewModelScope.launch {
_testItems.update { it.copy(isLoading = true) } try {
viewModelScope.launch { val result = getTestItemByGroupId.invoke(id);
try { _testItems.update {
val result = getTestItemByGroupId.invoke(id); it.copy(
_testItems.update { isLoading = false,
it.copy( testItem = result
isLoading = false, )
testItem = result }
) } catch (e:HttpException){
} handleError(e.message)
} catch (e:HttpException){ }
handleError(e.message) }
} }
} private suspend fun handleError(message: String?) {
} _testGroups.update {
private suspend fun handleError(message: String?) { it.copy(
_testGroups.update { isLoading = false,
it.copy( testGroup = null,
isLoading = false, error = message
testGroup = null, )
error = message }
) }
}
} }
} data class TestGroupItem(
val isLoading: Boolean = false,
data class TestGroupItem( val testGroup: Flow<List<TestGroup>>? = null,
val isLoading: Boolean = false, val error:String? = null
val testGroup: Flow<List<TestGroup>>? = null, )
val error:String? = null
) data class TestItemState(
val isLoading: Boolean = false,
data class TestItemState( val testItem: Flow<List<TestItem>>? = null,
val isLoading: Boolean = false, val error:String? = null
val testItem: Flow<List<TestItem>>? = null, )
val error:String? = null data class SearchResultState(
) val isLoading: Boolean = false,
data class SearchResultState( val searchResults: SearchResult? = null,
val isLoading: Boolean = false, val error: String? = null,
val searchResults: SearchResult? = null, val query: String = ""
val error: String? = null, )
val query: String = ""
) data class SearchResult(
val groups: List<TestGroup>,
data class SearchResult( val items: List<TestItem>
val groups: List<TestGroup>, )
val items: List<TestItem>
)
@@ -1,12 +1,12 @@
package com.approagency.drug.presentation.viewModel package com.approagency.pharmacy.presentation.viewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.approagency.drug.domain.model.PharmacyDetail import com.approagency.pharmacy.domain.model.PharmacyDetail
import com.approagency.drug.domain.model.PharmacyItem import com.approagency.pharmacy.domain.model.PharmacyItem
import com.approagency.drug.domain.usecase.GetPharmaciesUseCase import com.approagency.pharmacy.domain.usecase.GetPharmaciesUseCase
import com.approagency.drug.domain.usecase.GetPharmacyDetailUseCase import com.approagency.pharmacy.domain.usecase.GetPharmacyDetailUseCase
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -5,9 +5,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.approagency.drug.domain.model.DrugSearchResult import com.approagency.pharmacy.domain.model.DrugSearchResult
import com.approagency.drug.domain.usecase.SearchDrugsYabUseCase import com.approagency.pharmacy.domain.usecase.SearchDrugsYabUseCase
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -1,4 +1,4 @@
package com.approagency.drug.ui.theme package com.approagency.pharmacy.ui.theme
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -1,20 +1,20 @@
package com.vada.caller.ui.theme package com.vada.caller.ui.theme
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
data class Dime ( data class Dime (
val xxs: Dp = 2.dp, val xxs: Dp = 2.dp,
val xs: Dp = 4.dp, val xs: Dp = 4.dp,
val sm: Dp = 8.dp, val sm: Dp = 8.dp,
val md: Dp = 12.dp, val md: Dp = 12.dp,
val lg: Dp = 16.dp, val lg: Dp = 16.dp,
val xl: Dp = 20.dp, val xl: Dp = 20.dp,
val xxl: Dp = 24.dp, val xxl: Dp = 24.dp,
val iconSm: Dp = 20.dp, val iconSm: Dp = 20.dp,
val iconMd: Dp = 24.dp, val iconMd: Dp = 24.dp,
val iconLg: Dp = 32.dp, val iconLg: Dp = 32.dp,
) )
@@ -1,5 +1,5 @@
package com.vada.caller.ui.theme package com.vada.caller.ui.theme
import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.runtime.staticCompositionLocalOf
val LocalDime = staticCompositionLocalOf { Dime() } val LocalDime = staticCompositionLocalOf { Dime() }
@@ -1,8 +1,8 @@
package com.vada.caller.ui.theme package com.vada.caller.ui.theme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
val MaterialTheme.dime: Dime val MaterialTheme.dime: Dime
@Composable @Composable
get() =LocalDime.current get() =LocalDime.current
@@ -1,12 +1,12 @@
package com.fabirt.podcastapp.ui.theme package com.fabirt.podcastapp.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
val Shapes = Shapes( val Shapes = Shapes(
small = RoundedCornerShape(4.dp), small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(12.dp), medium = RoundedCornerShape(12.dp),
large = RoundedCornerShape(16.dp), large = RoundedCornerShape(16.dp),
extraLarge = RoundedCornerShape(20.dp) extraLarge = RoundedCornerShape(20.dp)
) )
@@ -1,10 +1,8 @@
package com.approagency.drug.ui.theme package com.approagency.pharmacy.ui.theme
import android.app.Activity
import android.os.Build import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.darkColorScheme import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.dynamicLightColorScheme
@@ -1,7 +1,5 @@
package com.approagency.drug.ui.theme package com.approagency.pharmacy.ui.theme
import android.content.res.Resources import com.approagency.pharmacy.R
import androidx.compose.material3.MaterialTheme
import com.approagency.drug.R
import androidx.compose.material3.Typography import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.Font
@@ -1,6 +1,6 @@
package com.approagency.drug.utils package com.approagency.pharmacy.utils
object Config { object Config {
const val BASE_URL = "https://drug.approagency.ir/api/" const val BASE_URL = "https://drug.approagency.ir/api/"
const val Darro_Url = "https://www.darooyab.ir/" const val Darro_Url = "https://www.darooyab.ir/"
} }
@@ -1,4 +1,4 @@
package com.approagency.drug.utils package com.approagency.pharmacy.utils
val provinces = listOf( val provinces = listOf(
@@ -1,29 +1,29 @@
package com.approagency.drug.utils package com.approagency.pharmacy.utils
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
suspend fun <T> retryWithBackoff( suspend fun <T> retryWithBackoff(
maxRetries: Int = 3, maxRetries: Int = 3,
initialDelayMs: Long = 1000, initialDelayMs: Long = 1000,
maxDelayMs: Long = 5000, maxDelayMs: Long = 5000,
factor: Double = 2.0, factor: Double = 2.0,
onRetry: ((attempt: Int, delayMs: Long) -> Unit)? = null, onRetry: ((attempt: Int, delayMs: Long) -> Unit)? = null,
block: suspend () -> T block: suspend () -> T
): Result<T> { ): Result<T> {
var currentDelay = initialDelayMs var currentDelay = initialDelayMs
repeat(maxRetries - 1) { attempt -> repeat(maxRetries - 1) { attempt ->
try { try {
return Result.success(block()) return Result.success(block())
} catch (e: Exception) { } catch (e: Exception) {
onRetry?.invoke(attempt + 1, currentDelay) onRetry?.invoke(attempt + 1, currentDelay)
delay(currentDelay) delay(currentDelay)
currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelayMs) currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelayMs)
} }
} }
// Last attempt // Last attempt
return try { return try {
Result.success(block()) Result.success(block())
} catch (e: Exception) { } catch (e: Exception) {
Result.failure(e) Result.failure(e)
} }
} }
+1 -1
View File
@@ -1,3 +1,3 @@
<resources> <resources>
<string name="app_name">drug</string> <string name="app_name">داروخانه همراه</string>
</resources> </resources>
@@ -1,4 +1,4 @@
package com.approagency.drug package com.approagency.pharmacy
import org.junit.Test import org.junit.Test