diff --git a/app/src/main/java/com/approagency/drug/data/remote/DarooyabApiService.kt b/app/src/main/java/com/approagency/drug/data/remote/DarooyabApiService.kt index a9386d0..fcb595a 100644 --- a/app/src/main/java/com/approagency/drug/data/remote/DarooyabApiService.kt +++ b/app/src/main/java/com/approagency/drug/data/remote/DarooyabApiService.kt @@ -32,4 +32,20 @@ interface DarooyabApiService { suspend fun getDrugDetail( @Path(value = "detailUrl", encoded = true) detailUrl: String ): String + + + @POST("Home/partialPatientReferralList") + @FormUrlEncoded + @Headers( + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36", + "X-Requested-With: XMLHttpRequest", + "Accept: */*", + "Accept-Language: en-US,en;q=0.9,fa;q=0.8" + ) + suspend fun getPharmacies( + @Field("brandIrc") brandIrc: String, + @Field("genericDrugId") genericDrugId: String, + @Field("provId") provId: String, + @Field("cityId") cityId: String + ): String // returns HTML } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/remote/PharmacyHtmlParser.kt b/app/src/main/java/com/approagency/drug/data/remote/PharmacyHtmlParser.kt new file mode 100644 index 0000000..011073e --- /dev/null +++ b/app/src/main/java/com/approagency/drug/data/remote/PharmacyHtmlParser.kt @@ -0,0 +1,59 @@ +package com.approagency.drug.data.remote +import com.approagency.drug.domain.model.PharmacyItem +import org.jsoup.Jsoup + +object PharmacyHtmlParser { + + fun parse(html: String): List { + val items = mutableListOf() + val doc = Jsoup.parse(html) + val rows = doc.select("table#TBL_patientReferral tbody tr") + + for (row in rows) { + val cells = row.select("td") + if (cells.size < 3) continue + + // ستون 0: اطلاعات برند + val brandCell = cells[0] + val brandLink = brandCell.select("a").first() + val brandName = brandLink?.text()?.trim() ?: "" + val brandUrl = brandLink?.attr("href")?.trim() ?: "" + val brandId = extractIdFromUrl(brandUrl, "/B-(\\d+)/?") + + // ستون 1: اطلاعات داروخانه + val pharmacyCell = cells[1] + val pharmacyLink = pharmacyCell.select("a").first() + val pharmacyName = pharmacyLink?.text()?.trim() ?: "" + val pharmacyUrl = pharmacyLink?.attr("href")?.trim() ?: "" + val pharmacyId = extractIdFromUrl(pharmacyUrl, "/ph-(\\d+)/?") + + // ستون 2: موقعیت + val locationText = cells[2].text().trim() + val parts = locationText.split("شهر") + val province = parts.getOrNull(0)?.replace("استان", "")?.trim() ?: "" + val city = parts.getOrNull(1)?.trim() ?: "" + + if (brandName.isNotBlank() && pharmacyName.isNotBlank()) { + items.add( + PharmacyItem( + brandName = brandName, + brandUrl = brandUrl, + brandId = brandId, + pharmacyName = pharmacyName, + pharmacyUrl = pharmacyUrl, + pharmacyId = pharmacyId, + province = province, + city = city + ) + ) + } + } + + return items + } + + private fun extractIdFromUrl(url: String, pattern: String): String { + val regex = pattern.toRegex() + return regex.find(url)?.groupValues?.get(1) ?: "" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/repository/DrugRepositoryImpl.kt b/app/src/main/java/com/approagency/drug/data/repository/DrugRepositoryImpl.kt index 5e8e0a3..baaaa15 100644 --- a/app/src/main/java/com/approagency/drug/data/repository/DrugRepositoryImpl.kt +++ b/app/src/main/java/com/approagency/drug/data/repository/DrugRepositoryImpl.kt @@ -7,11 +7,13 @@ import com.approagency.drug.data.remote.DarooyabApiService import com.approagency.drug.data.remote.DrugApiService import com.approagency.drug.data.remote.DrugDetailParser import com.approagency.drug.data.remote.DrugHtmlParser +import com.approagency.drug.data.remote.PharmacyHtmlParser 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.PharmacyItem import com.approagency.drug.domain.repository.DrugRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -89,4 +91,23 @@ class DrugRepositoryImpl( } } } + + override suspend fun searchPharmacies( + genericDrugId: String, + provId: String + ): List { + return withContext(Dispatchers.IO) { + try { + val html = darooyabApiService.getPharmacies( + brandIrc = "0", + genericDrugId = genericDrugId, + provId = provId, + cityId = "0" + ) + PharmacyHtmlParser.parse(html) + } catch (e: Exception) { + emptyList() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/di/AppModule.kt b/app/src/main/java/com/approagency/drug/di/AppModule.kt index f86dbd2..c46774d 100644 --- a/app/src/main/java/com/approagency/drug/di/AppModule.kt +++ b/app/src/main/java/com/approagency/drug/di/AppModule.kt @@ -14,6 +14,7 @@ import com.approagency.drug.domain.usecase.DrugDetailYabUseCase import com.approagency.drug.domain.usecase.GetDarmanUseCase import com.approagency.drug.domain.usecase.GetDrugDetailUseCase import com.approagency.drug.domain.usecase.GetDrugSearchUseCase +import com.approagency.drug.domain.usecase.GetPharmaciesUseCase import com.approagency.drug.domain.usecase.GetTestGroupUseCase import com.approagency.drug.domain.usecase.GetTestItemByGroupId import com.approagency.drug.domain.usecase.SearchDrugsYabUseCase @@ -21,6 +22,7 @@ import com.approagency.drug.domain.usecase.SearchTestsUseCase import com.approagency.drug.presentation.viewModel.DrugDetailViewModel import com.approagency.drug.presentation.viewModel.HomeViewModel import com.approagency.drug.presentation.viewModel.LabViewModel +import com.approagency.drug.presentation.viewModel.PharmacyViewModel import com.approagency.drug.utils.Config import com.approgency.drug.presentation.viewModel.SearchViewModel import okhttp3.OkHttpClient @@ -119,6 +121,10 @@ val appModule= module { GetTestGroupUseCase(get()) } + single { + GetPharmaciesUseCase(get()) + } + single { GetTestItemByGroupId(get()) } @@ -142,6 +148,9 @@ val appModule= module { viewModel { SearchViewModel(get()) } + viewModel { + PharmacyViewModel(get()) + } viewModel { LabViewModel(get() , get() , get()) diff --git a/app/src/main/java/com/approagency/drug/domain/model/PharmacyModels.kt b/app/src/main/java/com/approagency/drug/domain/model/PharmacyModels.kt new file mode 100644 index 0000000..c59984d --- /dev/null +++ b/app/src/main/java/com/approagency/drug/domain/model/PharmacyModels.kt @@ -0,0 +1,21 @@ +package com.approagency.drug.domain.model + +data class PharmacySearchRequest( + val brandIrc: String = "0", // برای صفحات برند + val genericDrugId: String, // برای صفحات ژنریک + val provId: String, // ID استان (0 برای همه) + val cityId: String = "0" // ID شهر (0 برای همه) +) + +data class PharmacyItem( + val brandName: String, // نام برند دارو + val brandUrl: String, // لینک صفحه برند + val brandId: String, // IRC برند + + val pharmacyName: String, // نام داروخانه + val pharmacyUrl: String, // لینک داروخانه + val pharmacyId: String, // ID داروخانه + + val province: String, // نام استان + val city: String // نام شهر +) \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/repository/DrugRepository.kt b/app/src/main/java/com/approagency/drug/domain/repository/DrugRepository.kt index 0ae75ef..73331ed 100644 --- a/app/src/main/java/com/approagency/drug/domain/repository/DrugRepository.kt +++ b/app/src/main/java/com/approagency/drug/domain/repository/DrugRepository.kt @@ -8,6 +8,7 @@ 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.PharmacyItem interface DrugRepository { suspend fun searchDrug(params: DrugSearchParams): Result @@ -15,5 +16,6 @@ interface DrugRepository { suspend fun getGorohDaroei(): Result suspend fun searchDrugs(params:DaroYabParams):Result suspend fun getDrugDetailFromYab(detailUrl: String):Result + suspend fun searchPharmacies(genericDrugId: String,provId: String): List } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/usecase/GetPharmaciesUseCase.kt b/app/src/main/java/com/approagency/drug/domain/usecase/GetPharmaciesUseCase.kt new file mode 100644 index 0000000..44f5df0 --- /dev/null +++ b/app/src/main/java/com/approagency/drug/domain/usecase/GetPharmaciesUseCase.kt @@ -0,0 +1,15 @@ +package com.approagency.drug.domain.usecase + +import com.approagency.drug.domain.model.PharmacyItem +import com.approagency.drug.domain.repository.DrugRepository + +class GetPharmaciesUseCase( + private val repository: DrugRepository +) { + suspend operator fun invoke( + genericDrugId: String, + provinceId: String + ): List { + return repository.searchPharmacies(genericDrugId, provinceId) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/navigation/AppNavGraph.kt b/app/src/main/java/com/approagency/drug/navigation/AppNavGraph.kt index 0a93b7e..0be624c 100644 --- a/app/src/main/java/com/approagency/drug/navigation/AppNavGraph.kt +++ b/app/src/main/java/com/approagency/drug/navigation/AppNavGraph.kt @@ -6,7 +6,6 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.compose.navigation import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import com.approagency.drug.presentation.screens.DrugDetailScreen @@ -36,7 +35,7 @@ fun AppNavGraph() { composable("search") { MainContainer(navController) { - SearchScreen(navController) + SearchScreen(navController,) } } diff --git a/app/src/main/java/com/approagency/drug/presentation/components/DaroYabResultContent.kt b/app/src/main/java/com/approagency/drug/presentation/components/DaroYabResultContent.kt index 58d5c20..286864f 100644 --- a/app/src/main/java/com/approagency/drug/presentation/components/DaroYabResultContent.kt +++ b/app/src/main/java/com/approagency/drug/presentation/components/DaroYabResultContent.kt @@ -27,7 +27,7 @@ import com.vada.caller.ui.theme.dime fun DaroYabSearchResult( drug: DrugSearchResult, onClickDetail: (DrugSearchResult) -> Unit, - onClickDrugStore:() ->Unit, + onClickDrugStore:(DrugSearchResult) ->Unit, modifier: Modifier = Modifier ) { val dime = LocalDime.current @@ -71,7 +71,7 @@ fun DaroYabSearchResult( TextButton(onClick = {onClickDetail(drug)} , content = { Text("اطلاعات دارویی") }) - TextButton(onClick = {onClickDetail(drug)} , content = { + TextButton(onClick = {onClickDrugStore(drug)} , content = { Text("موجودی داروخانه") }) } diff --git a/app/src/main/java/com/approagency/drug/presentation/components/PharmacyBottomSheet.kt b/app/src/main/java/com/approagency/drug/presentation/components/PharmacyBottomSheet.kt new file mode 100644 index 0000000..796773b --- /dev/null +++ b/app/src/main/java/com/approagency/drug/presentation/components/PharmacyBottomSheet.kt @@ -0,0 +1,269 @@ +package com.approagency.drug.presentation.components + + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.approagency.drug.domain.model.PharmacyItem +import com.approagency.drug.presentation.common.CustomModalBottomSheet +import com.approagency.drug.presentation.viewModel.PharmacyState +import com.approagency.drug.presentation.viewModel.PharmacyViewModel +import com.vada.caller.ui.theme.LocalDime +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun PharmacyBottomSheet( + viewModel: PharmacyViewModel, + genericDrugId: String, + isOpen: Boolean, + onDismiss: () -> Unit +) { + val scope = rememberCoroutineScope() + val sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true, + confirmValueChange = { true } + ) + + // لیست استان‌ها (همان لیست HTML) + val provinces = listOf( + Province("0", "همه استان ها"), + Province("1", "اصفهان"), + Province("40", "تهران"), + Province("137", "آذربایجان شرقی"), + Province("138", "آذربایجان غربی"), + Province("139", "اردبیل"), + Province("140", "البرز"), + Province("141", "ایلام"), + Province("142", "بوشهر"), + Province("143", "چهارمحال و بختیاری"), + Province("144", "خراسان جنوبی"), + Province("145", "خراسان رضوی"), + Province("146", "خراسان شمالی"), + Province("147", "خوزستان"), + Province("148", "زنجان"), + Province("149", "سمنان"), + Province("150", "سیستان و بلوچستان"), + Province("151", "فارس"), + Province("152", "قزوین"), + Province("153", "قم"), + Province("154", "کردستان"), + Province("155", "کرمان"), + Province("156", "کرمانشاه"), + Province("157", "کهگیلویه و بویراحمد"), + Province("158", "گلستان"), + Province("159", "گیلان"), + Province("160", "لرستان"), + Province("161", "مازندران"), + Province("162", "مرکزی"), + Province("163", "هرمزگان"), + Province("164", "همدان"), + Province("165", "یزد") + ) + + var selectedProvinceId by remember { mutableStateOf("40") } // پیش‌فرض تهران + var showProvinceDropdown by remember { mutableStateOf(false) } + + val pharmacyState by viewModel.state.collectAsState() + + // جستجوی اولیه با استان تهران + LaunchedEffect(Unit) { + if (genericDrugId.isNotBlank()) { + viewModel.search(genericDrugId, "40") + } + } + + if (isOpen) { + CustomModalBottomSheet( + sheetState = sheetState, + onDismiss = onDismiss, + scope = scope + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // عنوان + Text( + text = "جستجوی دارو در داروخانه‌ها", + fontWeight = FontWeight.Bold, + fontSize = MaterialTheme.typography.titleMedium.fontSize + ) + + // انتخاب استان + ExposedDropdownMenuBox( + expanded = showProvinceDropdown, + onExpandedChange = { showProvinceDropdown = it } + ) { + OutlinedTextField( + value = provinces.find { it.id == selectedProvinceId }?.name ?: "انتخاب استان", + onValueChange = {}, + readOnly = true, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = showProvinceDropdown) }, + modifier = Modifier + .fillMaxWidth() + .menuAnchor(), + shape = MaterialTheme.shapes.small + ) + + ExposedDropdownMenu( + expanded = showProvinceDropdown, + onDismissRequest = { showProvinceDropdown = false } + ) { + provinces.forEach { province -> + DropdownMenuItem( + text = { Text(province.name) }, + onClick = { + selectedProvinceId = province.id + showProvinceDropdown = false + // جستجو با استان جدید + viewModel.search(genericDrugId, province.id) + } + ) + } + } + } + + Spacer(modifier = Modifier.height(8.dp)) + + // نمایش نتایج + when (val state = pharmacyState) { + PharmacyState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(200.dp), + contentAlignment = Alignment.Center + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + CircularProgressIndicator( + modifier = Modifier.size(32.dp) + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "در حال جستجو...", + fontSize = MaterialTheme.typography.bodySmall.fontSize + ) + } + } + } + + is PharmacyState.Success -> { + if (state.items.isEmpty()) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(200.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = "هیچ داروخانه‌ای یافت نشد", + fontSize = MaterialTheme.typography.bodyMedium.fontSize, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) + ) + } + } else { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.heightIn(max = 400.dp) + ) { + items(state.items) { pharmacy -> + PharmacyResultItem(pharmacy = pharmacy) + } + + item { + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = "تعداد کل: ${state.items.size} داروخانه", + fontSize = MaterialTheme.typography.bodySmall.fontSize, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), + modifier = Modifier.padding(bottom = 8.dp) + ) + } + } + } + } + + is PharmacyState.Error -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(200.dp), + contentAlignment = Alignment.Center + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text( + text = state.message, + color = MaterialTheme.colorScheme.error, + fontSize = MaterialTheme.typography.bodyMedium.fontSize + ) + Spacer(modifier = Modifier.height(8.dp)) + TextButton( + onClick = { viewModel.retry() } + ) { + Text("تلاش مجدد") + } + } + } + } + + PharmacyState.Idle -> {} + } + } + } + } +} + +@Composable +fun PharmacyResultItem( + pharmacy: PharmacyItem, + modifier: Modifier = Modifier +) { + val dime = LocalDime.current + + Card( + modifier = modifier.fillMaxWidth(), + elevation = CardDefaults.cardElevation(defaultElevation = 1.dp) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(dime.md), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + // نام برند + Text( + text = pharmacy.brandName, + fontSize = MaterialTheme.typography.bodyMedium.fontSize, + color = MaterialTheme.colorScheme.primary + ) + + // نام داروخانه + Text( + text = pharmacy.pharmacyName, + fontWeight = FontWeight.Bold, + fontSize = MaterialTheme.typography.bodyLarge.fontSize + ) + + // موقعیت + Text( + text = "${pharmacy.province} - ${pharmacy.city}", + fontSize = MaterialTheme.typography.bodySmall.fontSize, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) + ) + } + } +} + +data class Province( + val id: String, + val name: String +) \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/screens/RootScreen.kt b/app/src/main/java/com/approagency/drug/presentation/screens/RootScreen.kt index 41a8513..c60d65f 100644 --- a/app/src/main/java/com/approagency/drug/presentation/screens/RootScreen.kt +++ b/app/src/main/java/com/approagency/drug/presentation/screens/RootScreen.kt @@ -2,19 +2,14 @@ package com.approagency.drug.presentation.screens import android.annotation.SuppressLint import android.app.Activity -import android.graphics.drawable.Icon -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Build @@ -24,12 +19,10 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarDefaults import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.Scaffold import androidx.compose.material3.ScaffoldDefaults -import androidx.compose.material3.SegmentedButtonDefaults.Icon import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults @@ -41,7 +34,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.text.font.FontWeight @@ -123,7 +115,10 @@ fun RootScreen(navHostController: NavHostController, modifier: Modifier){ padding -> when (seletedTab) { 0 -> HomeScreen( navController = navHostController,Modifier.padding(padding).consumeWindowInsets(padding)) 1 -> LabScreen(Modifier.padding(padding)) - 2 -> SearchScreen(navHostController,modifier = Modifier.padding(padding).consumeWindowInsets(padding)) + 2 -> SearchScreen( + navHostController, + modifier = Modifier.padding(padding).consumeWindowInsets(padding), + ) } } } diff --git a/app/src/main/java/com/approagency/drug/presentation/screens/SearchScreen.kt b/app/src/main/java/com/approagency/drug/presentation/screens/SearchScreen.kt index fec7b0b..29bc1b5 100644 --- a/app/src/main/java/com/approagency/drug/presentation/screens/SearchScreen.kt +++ b/app/src/main/java/com/approagency/drug/presentation/screens/SearchScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.navigation.NavController +import com.approagency.drug.domain.model.DrugSearchResult import com.approagency.drug.navigation.Screen import com.approagency.drug.presentation.common.CustomTextFilled import com.approagency.drug.presentation.common.EmptySearchState @@ -33,7 +34,9 @@ import com.approagency.drug.presentation.common.LoadingMoreIndicator import com.approagency.drug.presentation.common.PrimaryButton import com.approagency.drug.presentation.components.DaroYabSearchResult import com.approagency.drug.presentation.components.DrugDetailBottomSheet +import com.approagency.drug.presentation.components.PharmacyBottomSheet import com.approagency.drug.presentation.viewModel.DrugDetailViewModel +import com.approagency.drug.presentation.viewModel.PharmacyViewModel import com.approgency.drug.presentation.viewModel.SearchState import com.approgency.drug.presentation.viewModel.SearchViewModel import com.vada.caller.ui.theme.LocalDime @@ -46,7 +49,8 @@ fun SearchScreen( navController: NavController, modifier: Modifier = Modifier, viewModel: SearchViewModel = koinViewModel(), - drugDetailViewModel: DrugDetailViewModel = koinViewModel() // اضافه کنید + drugDetailViewModel: DrugDetailViewModel = koinViewModel(), + pharmacyViewModel: PharmacyViewModel = koinViewModel(), ) { val dime = LocalDime.current val keyboardController = LocalSoftwareKeyboardController.current @@ -54,6 +58,10 @@ fun SearchScreen( val state by viewModel.searchState.collectAsState() val lazyListState = rememberLazyListState() var selectedDrugUrl by remember { mutableStateOf(null) } + + var showPharmacyBottomSheet by remember { mutableStateOf(false) } + var selectedDrugForPharmacy by remember { mutableStateOf(null) } + // Auto-search for testing (remove in production) // LaunchedEffect(Unit) { // if (searchText.isEmpty()) { @@ -118,6 +126,22 @@ fun SearchScreen( ) Spacer(modifier = Modifier.height(MaterialTheme.dime.md)) + + if (showPharmacyBottomSheet && selectedDrugForPharmacy != null) { + // استخراج genericDrugId از URL + // مثال: /G-799/Fexofenadine -> 799 + val genericId = extractGenericIdFromUrl(selectedDrugForPharmacy?.detailPageUrl ?: "") + + PharmacyBottomSheet( + viewModel = pharmacyViewModel, + genericDrugId = genericId, + isOpen = showPharmacyBottomSheet, + onDismiss = { + showPharmacyBottomSheet = false + selectedDrugForPharmacy = null + } + ) + } // Results area when (val currentState = state) { is SearchState.Loading -> { @@ -148,7 +172,10 @@ fun SearchScreen( ) ) }, - onClickDrugStore = {} + onClickDrugStore = { selectedDrug -> + selectedDrugForPharmacy = selectedDrug + showPharmacyBottomSheet = true + } ) } item { @@ -172,7 +199,10 @@ fun SearchScreen( ) ) }, - onClickDrugStore = {} + onClickDrugStore = { selectedDrug -> + selectedDrugForPharmacy = selectedDrug + showPharmacyBottomSheet = true + } ) } @@ -205,4 +235,10 @@ fun SearchScreen( } } } +} + +// تابع کمکی برای استخراج ID از URL +private fun extractGenericIdFromUrl(url: String): String { + val pattern = "/G-(\\d+)/".toRegex() + return pattern.find(url)?.groupValues?.get(1) ?: "" } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/viewModel/PharmacyViewModel.kt b/app/src/main/java/com/approagency/drug/presentation/viewModel/PharmacyViewModel.kt new file mode 100644 index 0000000..cb0cd82 --- /dev/null +++ b/app/src/main/java/com/approagency/drug/presentation/viewModel/PharmacyViewModel.kt @@ -0,0 +1,52 @@ +package com.approagency.drug.presentation.viewModel + + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.approagency.drug.domain.model.PharmacyItem +import com.approagency.drug.domain.usecase.GetPharmaciesUseCase +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +sealed class PharmacyState { + object Idle : PharmacyState() + object Loading : PharmacyState() + data class Success(val items: List) : PharmacyState() + data class Error(val message: String) : PharmacyState() +} + +class PharmacyViewModel( + private val getPharmaciesUseCase: GetPharmaciesUseCase +) : ViewModel() { + + private val _state = MutableStateFlow(PharmacyState.Idle) + val state: StateFlow = _state.asStateFlow() + + private var currentGenericDrugId: String = "" + private var currentProvinceId: String = "0" + + fun search(genericDrugId: String, provinceId: String) { + if (genericDrugId.isBlank()) return + + currentGenericDrugId = genericDrugId + currentProvinceId = provinceId + + viewModelScope.launch { + _state.value = PharmacyState.Loading + try { + val result = getPharmaciesUseCase(genericDrugId, provinceId) + _state.value = PharmacyState.Success(result) + } catch (e: Exception) { + _state.value = PharmacyState.Error(e.message ?: "خطا در دریافت اطلاعات") + } + } + } + + fun retry() { + if (currentGenericDrugId.isNotBlank()) { + search(currentGenericDrugId, currentProvinceId) + } + } +} \ No newline at end of file