style: make better style
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
package com.approagency.drug.data.remote
|
||||
|
||||
import com.approagency.drug.domain.model.PharmacyItem
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
@@ -7,31 +8,48 @@ object PharmacyHtmlParser {
|
||||
fun parse(html: String): List<PharmacyItem> {
|
||||
val items = mutableListOf<PharmacyItem>()
|
||||
val doc = Jsoup.parse(html)
|
||||
val rows = doc.select("table#TBL_patientReferral tbody tr")
|
||||
val table = doc.select("table#TBL_patientReferral").first() ?: return emptyList()
|
||||
|
||||
// تشخیص ساختار بر اساس سربرگ جدول (یک بار برای کل جدول)
|
||||
val hasGuaranteeColumn = hasGuaranteeColumnInHeader(table)
|
||||
|
||||
val rows = table.select("tbody tr")
|
||||
|
||||
for (row in rows) {
|
||||
val cells = row.select("td")
|
||||
if (cells.size < 3) continue
|
||||
|
||||
// ستون 0: اطلاعات برند
|
||||
val brandCell = cells[0]
|
||||
// ایندکس ستونها بر اساس وجود ستون تضمین
|
||||
val brandIndex = if (hasGuaranteeColumn) 1 else 0
|
||||
val pharmacyIndex = if (hasGuaranteeColumn) 2 else 1
|
||||
val locationIndex = if (hasGuaranteeColumn) 3 else 2
|
||||
|
||||
// ستون برند
|
||||
val brandCell = cells[brandIndex]
|
||||
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 pharmacyCell = cells[pharmacyIndex]
|
||||
val pharmacyLinks = pharmacyCell.select("a")
|
||||
|
||||
// پیدا کردن لینک داروخانه (لینکی که "اطلاعات تماس" نباشد)
|
||||
val pharmacyLink = pharmacyLinks.firstOrNull {
|
||||
!it.text().contains("اطلاعات تماس")
|
||||
}
|
||||
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() ?: ""
|
||||
// ستون موقعیت
|
||||
val locationCell = cells[locationIndex]
|
||||
val locationText = locationCell.text().trim()
|
||||
|
||||
// استخراج استان و شهر
|
||||
val province = extractProvince(locationText)
|
||||
val city = extractCity(locationText)
|
||||
|
||||
if (brandName.isNotBlank() && pharmacyName.isNotBlank()) {
|
||||
items.add(
|
||||
@@ -52,6 +70,32 @@ object PharmacyHtmlParser {
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* تشخیص وجود ستون تضمین موجودی با بررسی سربرگ جدول
|
||||
*/
|
||||
private fun hasGuaranteeColumnInHeader(table: org.jsoup.nodes.Element): Boolean {
|
||||
val headers = table.select("thead th")
|
||||
if (headers.isEmpty()) return false
|
||||
|
||||
// بررسی میکنیم که آیا اولین ستون حاوی متن "تضمین" است
|
||||
val firstHeaderText = headers.firstOrNull()?.text()?.trim() ?: ""
|
||||
return firstHeaderText.contains("تضمین") || firstHeaderText.contains("موجودی")
|
||||
}
|
||||
|
||||
private fun extractProvince(locationText: String): String {
|
||||
// الگو: "استان تهران" یا "استان اصفهان"
|
||||
val provincePattern = "استان\\s*(\\S+)".toRegex()
|
||||
val match = provincePattern.find(locationText)
|
||||
return match?.groupValues?.get(1)?.trim() ?: ""
|
||||
}
|
||||
|
||||
private fun extractCity(locationText: String): String {
|
||||
// الگو: "شهر تهران" یا "شهر اصفهان" یا "شهر پردیس"
|
||||
val cityPattern = "شهر\\s*(.+)".toRegex()
|
||||
val match = cityPattern.find(locationText)
|
||||
return match?.groupValues?.get(1)?.trim() ?: ""
|
||||
}
|
||||
|
||||
private fun extractIdFromUrl(url: String, pattern: String): String {
|
||||
val regex = pattern.toRegex()
|
||||
return regex.find(url)?.groupValues?.get(1) ?: ""
|
||||
|
||||
+127
-104
@@ -1,6 +1,7 @@
|
||||
package com.approagency.drug.presentation.components
|
||||
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
@@ -8,6 +9,7 @@ import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
@@ -15,12 +17,15 @@ import androidx.compose.ui.unit.dp
|
||||
import com.approagency.drug.domain.model.PharmacyItem
|
||||
import com.approagency.drug.presentation.common.CustomBox
|
||||
import com.approagency.drug.presentation.common.CustomModalBottomSheet
|
||||
import com.approagency.drug.presentation.common.Loading
|
||||
import com.approagency.drug.presentation.viewModel.PharmacyState
|
||||
import com.approagency.drug.presentation.viewModel.PharmacyViewModel
|
||||
import com.approagency.drug.utils.provinces
|
||||
import com.vada.caller.ui.theme.LocalDime
|
||||
import com.vada.caller.ui.theme.dime
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@SuppressLint("ConfigurationScreenWidthHeight")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PharmacyBottomSheet(
|
||||
@@ -55,135 +60,153 @@ fun PharmacyBottomSheet(
|
||||
scope = scope
|
||||
) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxHeight()
|
||||
.fillMaxWidth().heightIn(min = 400.dp)
|
||||
.padding(MaterialTheme.dime.md),
|
||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.xl)
|
||||
) {
|
||||
// عنوان
|
||||
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.large
|
||||
item {
|
||||
Text(
|
||||
text = "جستجوی دارو در داروخانهها",
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize
|
||||
)
|
||||
|
||||
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(
|
||||
item {
|
||||
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()
|
||||
.height(200.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
.menuAnchor(),
|
||||
shape = MaterialTheme.shapes.large
|
||||
)
|
||||
|
||||
ExposedDropdownMenu(
|
||||
expanded = showProvinceDropdown,
|
||||
onDismissRequest = { showProvinceDropdown = false }
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = "در حال جستجو...",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize
|
||||
provinces.forEach { province ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(province.name) },
|
||||
onClick = {
|
||||
selectedProvinceId = province.id
|
||||
showProvinceDropdown = false
|
||||
// جستجو با استان جدید
|
||||
viewModel.search(genericDrugId, province.id)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// انتخاب استان
|
||||
|
||||
|
||||
|
||||
|
||||
// نمایش نتایج
|
||||
when (val state = pharmacyState) {
|
||||
PharmacyState.Loading -> {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(400.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Loading()
|
||||
Spacer(modifier = Modifier.height(MaterialTheme.dime.xl))
|
||||
Text(
|
||||
text = "در حال جستجو...",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is PharmacyState.Success -> {
|
||||
if (state.items.isEmpty()) {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(400.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "هیچ داروخانهای یافت نشد",
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
items(state.items)
|
||||
{ pharmacy ->
|
||||
PharmacyResultItem(pharmacy = pharmacy)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(MaterialTheme.dime.md))
|
||||
Text(
|
||||
text = "تعداد کل: ${state.items.size} داروخانه",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.dime.xs)
|
||||
)
|
||||
}
|
||||
// LazyColumn(
|
||||
// verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
// modifier = Modifier,
|
||||
// ) {
|
||||
// items(state.items) { pharmacy ->
|
||||
// PharmacyResultItem(pharmacy = pharmacy)
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
is PharmacyState.Error -> {
|
||||
item {
|
||||
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.weight(1f),
|
||||
) {
|
||||
items(state.items) { pharmacy ->
|
||||
PharmacyResultItem(pharmacy = pharmacy)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = "تعداد کل: ${state.items.size} داروخانه",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
text = state.message,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
TextButton(
|
||||
onClick = { viewModel.retry() }
|
||||
) {
|
||||
Text("تلاش مجدد")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 -> {}
|
||||
@@ -201,7 +224,7 @@ fun PharmacyResultItem(
|
||||
) {
|
||||
val dime = LocalDime.current
|
||||
|
||||
CustomBox (
|
||||
CustomBox(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
Column(
|
||||
|
||||
Reference in New Issue
Block a user