feat: parse text so better
This commit is contained in:
@@ -4,19 +4,51 @@ import com.approagency.drug.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 {
|
||||||
|
|
||||||
fun parseDrugDetail(html: String): DrugDetail {
|
fun parseDrugDetail(html: String): DrugDetail {
|
||||||
val document = Jsoup.parse(html)
|
val document = Jsoup.parse(html)
|
||||||
|
|
||||||
// اطلاعات پایه
|
// تشخیص نوع صفحه: Generic (G) یا Brand (B)
|
||||||
|
val isGenericPage = detectPageType(document)
|
||||||
|
|
||||||
|
return if (isGenericPage) {
|
||||||
|
parseGenericPage(document)
|
||||||
|
} else {
|
||||||
|
parseBrandPage(document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* تشخیص نوع صفحه با بررسی URL canonical و ساختار DOM
|
||||||
|
*/
|
||||||
|
private fun detectPageType(document: Document): Boolean {
|
||||||
|
// روش 1: بررسی URL canonical
|
||||||
|
val canonicalUrl = document.select("link[rel=canonical]").attr("href")
|
||||||
|
if (canonicalUrl.contains("/G-")) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (canonicalUrl.contains("/B-")) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// روش 2: بررسی ساختار DOM (Fallback)
|
||||||
|
val hasGenericLayout = document.select("#UL_GenericTabInfo").isNotEmpty()
|
||||||
|
val hasBrandLayout = document.select("#BrandInfoContainer").isNotEmpty()
|
||||||
|
|
||||||
|
return hasGenericLayout || !hasBrandLayout // Default to generic if ambiguous
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Generic Page Parsing
|
||||||
|
// ============================================================
|
||||||
|
private fun parseGenericPage(document: Document): DrugDetail {
|
||||||
val genericId = extractGenericId(document)
|
val genericId = extractGenericId(document)
|
||||||
val persianName = extractPersianName(document)
|
val persianName = extractPersianName(document)
|
||||||
val englishName = extractEnglishName(document)
|
val englishName = extractEnglishName(document)
|
||||||
|
val sections = extractSectionsFromGeneric(document)
|
||||||
// استخراج تمام بخشها به صورت داینامیک
|
|
||||||
val sections = extractAllSections(document)
|
|
||||||
|
|
||||||
return DrugDetail(
|
return DrugDetail(
|
||||||
genericId = genericId,
|
genericId = genericId,
|
||||||
@@ -24,27 +56,30 @@ class DrugDetailParser {
|
|||||||
englishName = englishName,
|
englishName = englishName,
|
||||||
drugClass = extractDrugClass(document),
|
drugClass = extractDrugClass(document),
|
||||||
therapeuticClass = extractTherapeuticClass(document),
|
therapeuticClass = extractTherapeuticClass(document),
|
||||||
usage = sections["usage"] ?: extractSectionByText(document, "موارد مصرف"),
|
usage = sections["usage"],
|
||||||
mechanism = sections["mechanism"] ?: extractSectionByText(document, "مکانیسم اثر"),
|
mechanism = sections["mechanism"],
|
||||||
pharmacokinetics = sections["pharmacokinetics"] ?: extractSectionByText(document, "فارماکوکینتیک"),
|
pharmacokinetics = sections["pharmacokinetics"],
|
||||||
contraindications = sections["contraindications"] ?: extractSectionByText(document, "منع مصرف"),
|
contraindications = sections["contraindications"],
|
||||||
sideEffects = sections["sideEffects"] ?: extractSectionByText(document, "عوارض جانبی"),
|
sideEffects = sections["sideEffects"],
|
||||||
interactions = sections["interactions"] ?: extractSectionByText(document, "تداخلات دارویی"),
|
interactions = sections["interactions"],
|
||||||
warnings = sections["warnings"] ?: extractSectionByText(document, "هشدار"),
|
warnings = sections["warnings"],
|
||||||
recommendations = sections["recommendations"] ?: extractSectionByText(document, "توصیه"),
|
recommendations = sections["recommendations"],
|
||||||
pregnancyCategory = extractPregnancyCategory(document),
|
pregnancyCategory = extractPregnancyCategory(document),
|
||||||
pregnancyDescription = extractPregnancyDescription(document),
|
pregnancyDescription = extractPregnancyDescription(document),
|
||||||
dosageForms = extractDosageForms(document),
|
dosageForms = extractDosageForms(document),
|
||||||
brandNames = extractBrandNames(document),
|
brandNames = extractBrandNames(document),
|
||||||
similarDrugs = extractSimilarDrugs(document),
|
similarDrugs = extractSimilarDrugs(document),
|
||||||
categories = extractCategories(document),
|
categories = extractCategories(document),
|
||||||
comments = extractComments(document)
|
comments = extractComments(document),
|
||||||
|
manufacturer = null,
|
||||||
|
isGeneric = true,
|
||||||
|
generalInfo = extractGeneralInfoGeneric(document),
|
||||||
|
specializedInfo = extractSpecializedInfoGeneric(document),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractGenericId(document: Document): String {
|
private fun extractGenericId(document: Document): String {
|
||||||
val urlElement = document.select("link[rel=canonical]").first()
|
val url = document.select("link[rel=canonical]").attr("href")
|
||||||
val url = urlElement?.attr("href") ?: ""
|
|
||||||
val regex = "/G-(\\d+)/".toRegex()
|
val regex = "/G-(\\d+)/".toRegex()
|
||||||
return regex.find(url)?.groupValues?.get(1) ?: ""
|
return regex.find(url)?.groupValues?.get(1) ?: ""
|
||||||
}
|
}
|
||||||
@@ -52,124 +87,283 @@ class DrugDetailParser {
|
|||||||
private fun extractPersianName(document: Document): String {
|
private fun extractPersianName(document: Document): String {
|
||||||
val titleElement = document.select("h1.EnglishNumericFont").first()
|
val titleElement = document.select("h1.EnglishNumericFont").first()
|
||||||
val fullText = titleElement?.text() ?: ""
|
val fullText = titleElement?.text() ?: ""
|
||||||
return fullText.replace("چیست و برای چه مواردی استفاده می شود؟", "").trim()
|
// Remove the suffix "چیست و برای چه مواردی استفاده می شود؟"
|
||||||
|
return fullText.replace(Regex("\\s*چیست و برای چه مواردی استفاده می شود\\?\\s*"), "").trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractEnglishName(document: Document): String {
|
private fun extractEnglishName(document: Document): String {
|
||||||
val englishLabel = document.select("label.EnglishTopLabel").first()
|
return document.select("label.EnglishTopLabel").text().trim()
|
||||||
return englishLabel?.text()?.trim() ?: ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractDrugClass(document: Document): String? {
|
private fun extractDrugClass(document: Document): String? {
|
||||||
val classElement = document.select("#divExtraInfo > div:first-child a.ahref_Generic").first()
|
return document.select("#divExtraInfo > div:first-child a.ahref_Generic").first()?.text()?.trim()
|
||||||
return classElement?.text()?.trim()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractTherapeuticClass(document: Document): String? {
|
private fun extractTherapeuticClass(document: Document): String? {
|
||||||
val therapeuticElement = document.select("#divExtraInfo > div:last-child a.ahref_Generic").first()
|
// The therapeutic class can be a chain of links
|
||||||
return therapeuticElement?.text()?.trim()
|
val classLinks = document.select("#divExtraInfo > div:last-child a.ahref_Generic")
|
||||||
|
return if (classLinks.isNotEmpty()) classLinks.joinToString(" > ") { it.text().trim() } else null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* استخراج بخشها با استفاده از ID (روش قبلی)
|
* Generic pages store content in divs with IDs inside #EtelaatTakhasosiContent
|
||||||
|
* The titles are h2.h2_TabTitle, and content is everything until the next h2.
|
||||||
*/
|
*/
|
||||||
private fun extractAllSections(document: Document): Map<String, String> {
|
private fun extractSectionsFromGeneric(document: Document): Map<String, String> {
|
||||||
val sections = mutableMapOf<String, String>()
|
val sections = mutableMapOf<String, String>()
|
||||||
|
val specializedContentDiv = document.getElementById("EtelaatTakhasosiContent") ?: return sections
|
||||||
|
|
||||||
// نقشه ID به کلید
|
// Mapping of section title keywords to our data class keys
|
||||||
val idToKey = mapOf(
|
val titleToKey = mapOf(
|
||||||
"0" to "usage",
|
"موارد مصرف" to "usage",
|
||||||
"1" to "mechanism",
|
"مکانیسم اثر" to "mechanism",
|
||||||
"2" to "pharmacokinetics",
|
"فارماکوکینتیک" to "pharmacokinetics",
|
||||||
"3" to "contraindications",
|
"منع مصرف" to "contraindications",
|
||||||
"4" to "sideEffects",
|
"عوارض جانبی" to "sideEffects",
|
||||||
"5" to "interactions",
|
"تداخلات دارویی" to "interactions",
|
||||||
"6" to "warnings",
|
"هشدار" to "warnings",
|
||||||
"7" to "recommendations"
|
"توصیه های دارویی" to "recommendations"
|
||||||
)
|
)
|
||||||
|
|
||||||
for ((id, key) in idToKey) {
|
val sectionHeaders = specializedContentDiv.select("h2.h2_TabTitle")
|
||||||
val section = extractSectionById(document, id)
|
|
||||||
if (!section.isNullOrBlank()) {
|
for (header in sectionHeaders) {
|
||||||
sections[key] = section
|
val headerText = header.text().trim()
|
||||||
|
val sectionKey = titleToKey.entries.find { headerText.contains(it.key) }?.value
|
||||||
|
|
||||||
|
if (sectionKey != null) {
|
||||||
|
var contentElement = header.nextElementSibling()
|
||||||
|
val contentBuilder = StringBuilder()
|
||||||
|
|
||||||
|
while (contentElement != null && !contentElement.select("h2.h2_TabTitle").hasText()) {
|
||||||
|
// Extract clean text from various elements
|
||||||
|
val text = cleanText(contentElement)
|
||||||
|
if (text.isNotBlank()) {
|
||||||
|
contentBuilder.append(text).append("\n\n")
|
||||||
|
}
|
||||||
|
contentElement = contentElement.nextElementSibling()
|
||||||
|
}
|
||||||
|
|
||||||
|
val content = contentBuilder.toString().trim()
|
||||||
|
if (content.isNotEmpty()) {
|
||||||
|
sections[sectionKey] = content
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sections
|
return sections
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractSectionById(document: Document, sectionId: String): String? {
|
/**
|
||||||
val sectionElement = document.select("h2.h2_TabTitle#${sectionId}").first()
|
* General info for generic pages is inside #EtelaatOmomiVaTakhasosi > #EtelaatOmomi
|
||||||
if (sectionElement == null) return null
|
*/
|
||||||
|
private fun extractGeneralInfoGeneric(document: Document): String? {
|
||||||
|
val generalDiv = document.select("#EtelaatOmomiVaTakhasosi #EtelaatOmomi").first() ?: return null
|
||||||
|
// Clone the element to avoid modifying the original document
|
||||||
|
val clone = generalDiv.clone()
|
||||||
|
|
||||||
val content = StringBuilder()
|
// Remove the accordion (table of contents) as it's not part of the main content
|
||||||
var nextElement = sectionElement.nextElementSibling()
|
clone.select(".accordion").remove()
|
||||||
|
// Remove navigation boxes if any
|
||||||
|
clone.select("#nav_box_general").remove()
|
||||||
|
|
||||||
while (nextElement != null && !nextElement.select("h2.h2_TabTitle").hasText()) {
|
return cleanText(clone).trim().takeIf { it.isNotEmpty() }
|
||||||
if (nextElement.tagName() == "p" || nextElement.tagName() == "div") {
|
|
||||||
val text = cleanHtmlText(nextElement.text())
|
|
||||||
if (text.isNotBlank()) {
|
|
||||||
content.append(text).append("\n\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nextElement = nextElement.nextElementSibling()
|
|
||||||
}
|
|
||||||
|
|
||||||
return content.toString().trim().takeIf { it.isNotEmpty() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* استخراج بخش با جستجوی متن عنوان (روش جایگزین)
|
* Specialized info for generic pages is inside #EtelaatTakhasosiContent
|
||||||
*/
|
*/
|
||||||
private fun extractSectionByText(document: Document, titleKeyword: String): String? {
|
private fun extractSpecializedInfoGeneric(document: Document): String? {
|
||||||
// جستجوی هدر حاوی کلمه کلیدی
|
val specializedDiv = document.getElementById("EtelaatTakhasosiContent") ?: return null
|
||||||
val header = document.select("h2.h2_TabTitle, h3").firstOrNull {
|
val clone = specializedDiv.clone()
|
||||||
it.text().contains(titleKeyword, ignoreCase = true)
|
|
||||||
} ?: return null
|
|
||||||
|
|
||||||
val content = StringBuilder()
|
// Remove the table of contents
|
||||||
var nextElement = header.nextElementSibling()
|
clone.select(".accordion").remove()
|
||||||
|
// Remove the "دارو های هم گروه" section and sources if you don't want them in specializedInfo
|
||||||
|
clone.select("#Teammate").remove()
|
||||||
|
clone.select("#externalLinks").remove()
|
||||||
|
|
||||||
while (nextElement != null && !nextElement.select("h2.h2_TabTitle, h3").hasText()) {
|
return cleanText(clone).trim().takeIf { it.isNotEmpty() }
|
||||||
if (nextElement.tagName() == "p" || nextElement.tagName() == "div") {
|
}
|
||||||
val text = cleanHtmlText(nextElement.text())
|
|
||||||
|
// ============================================================
|
||||||
|
// Brand Page Parsing
|
||||||
|
// ============================================================
|
||||||
|
private fun parseBrandPage(document: Document): DrugDetail {
|
||||||
|
val brandId = extractBrandId(document)
|
||||||
|
val persianName = extractBrandPersianName(document)
|
||||||
|
val englishName = extractBrandEnglishName(document)
|
||||||
|
val manufacturer = extractManufacturer(document)
|
||||||
|
val genericInfo = extractGenericInfo(document)
|
||||||
|
|
||||||
|
// Extract all sections from .brandAttrPersDesc
|
||||||
|
val (introText, sections) = extractBrandSections(document)
|
||||||
|
|
||||||
|
return DrugDetail(
|
||||||
|
genericId = genericInfo?.genericId ?: "",
|
||||||
|
persianName = persianName,
|
||||||
|
englishName = englishName,
|
||||||
|
drugClass = genericInfo?.persianName, // Drug class is essentially the generic name
|
||||||
|
therapeuticClass = extractBrandTherapeuticClass(document),
|
||||||
|
usage = sections["usage"] ?: introText,
|
||||||
|
mechanism = sections["mechanism"],
|
||||||
|
pharmacokinetics = null,
|
||||||
|
contraindications = sections["contraindications"],
|
||||||
|
sideEffects = sections["sideEffects"],
|
||||||
|
interactions = sections["interactions"],
|
||||||
|
warnings = sections["warnings"],
|
||||||
|
recommendations = sections["recommendations"],
|
||||||
|
pregnancyCategory = null,
|
||||||
|
pregnancyDescription = null,
|
||||||
|
dosageForms = extractOtherBrandForms(document),
|
||||||
|
brandNames = listOf(), // This page itself is a brand
|
||||||
|
similarDrugs = emptyList(),
|
||||||
|
categories = null,
|
||||||
|
comments = extractComments(document),
|
||||||
|
manufacturer = manufacturer,
|
||||||
|
genericInfo = genericInfo,
|
||||||
|
otherBrandForms = extractOtherBrandForms(document),
|
||||||
|
isGeneric = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractBrandId(document: Document): String {
|
||||||
|
val url = document.select("link[rel=canonical]").attr("href")
|
||||||
|
val regex = "/B-(\\d+)/".toRegex()
|
||||||
|
return regex.find(url)?.groupValues?.get(1) ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractBrandPersianName(document: Document): String {
|
||||||
|
return document.select("#h1PersianName").text().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractBrandEnglishName(document: Document): String {
|
||||||
|
return document.select("#h2EnglishName").text().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractManufacturer(document: Document): String? {
|
||||||
|
val manufacturerLink = document.select("#divProducer a.ahref_Generic").first()
|
||||||
|
return manufacturerLink?.text()?.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractGenericInfo(document: Document): GenericInfo? {
|
||||||
|
val genericLink = document.select("#divAjzaContent a.ahref_Generic").first() ?: return null
|
||||||
|
val href = genericLink.attr("href")
|
||||||
|
val genericIdRegex = "/G-(\\d+)/".toRegex()
|
||||||
|
val genericId = genericIdRegex.find(href)?.groupValues?.get(1) ?: ""
|
||||||
|
|
||||||
|
return GenericInfo(
|
||||||
|
genericId = genericId,
|
||||||
|
persianName = genericLink.text().trim(),
|
||||||
|
detailUrl = "https://www.darooyab.ir$href"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractBrandTherapeuticClass(document: Document): String? {
|
||||||
|
val therapeuticContainer = document.select("#brand_desc > div:last-child").first()
|
||||||
|
val text = therapeuticContainer?.text() ?: ""
|
||||||
|
val regex = "طبقه بندی درمانی :\\s*(.+?)(?:\$|\\n)".toRegex()
|
||||||
|
return regex.find(text)?.groupValues?.get(1)?.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract all content from .brandAttrPersDesc.
|
||||||
|
* Returns a Pair: first is the introductory text (before any h2), second is a map of section title to content.
|
||||||
|
*/
|
||||||
|
private fun extractBrandSections(document: Document): Pair<String?, Map<String, String>> {
|
||||||
|
val container = document.select(".brandAttrPersDesc").first() ?: return Pair(null, emptyMap())
|
||||||
|
val sections = mutableMapOf<String, String>()
|
||||||
|
val titleToKey = mapOf(
|
||||||
|
"موارد مصرف" to "usage",
|
||||||
|
"مکانیسم اثر" to "mechanism",
|
||||||
|
"منع مصرف" to "contraindications",
|
||||||
|
"عوارض جانبی" to "sideEffects",
|
||||||
|
"تداخلات دارویی" to "interactions",
|
||||||
|
"هشدار" to "warnings",
|
||||||
|
"توصیه های دارویی" to "recommendations"
|
||||||
|
)
|
||||||
|
|
||||||
|
var introText: String? = null
|
||||||
|
var currentSectionKey: String? = null
|
||||||
|
val contentBuilder = StringBuilder()
|
||||||
|
|
||||||
|
for (child in container.children()) {
|
||||||
|
if (child.tagName() == "h2" || child.tagName() == "h3") {
|
||||||
|
// If we were building a previous section, save it
|
||||||
|
if (currentSectionKey != null && contentBuilder.isNotEmpty()) {
|
||||||
|
sections[currentSectionKey] = contentBuilder.toString().trim()
|
||||||
|
contentBuilder.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new section
|
||||||
|
val headerText = child.text().trim()
|
||||||
|
currentSectionKey = titleToKey.entries.find { headerText.contains(it.key) }?.value
|
||||||
|
} else {
|
||||||
|
val text = cleanText(child)
|
||||||
if (text.isNotBlank()) {
|
if (text.isNotBlank()) {
|
||||||
content.append(text).append("\n\n")
|
if (currentSectionKey == null) {
|
||||||
|
// This is introductory text before any h2
|
||||||
|
introText = introText?.let { "$it\n\n$text" } ?: text
|
||||||
|
} else {
|
||||||
|
contentBuilder.append(text).append("\n\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nextElement = nextElement.nextElementSibling()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return content.toString().trim().takeIf { it.isNotEmpty() }
|
// Save the last section if any
|
||||||
|
if (currentSectionKey != null && contentBuilder.isNotEmpty()) {
|
||||||
|
sections[currentSectionKey] = contentBuilder.toString().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair(introText, sections)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cleanHtmlText(text: String): String {
|
private fun extractOtherBrandForms(document: Document): List<DosageForm> {
|
||||||
return text
|
val forms = mutableListOf<DosageForm>()
|
||||||
.replace(Regex("\\s+"), " ")
|
val brandmateDiv = document.select(".brandmate").first() ?: return forms
|
||||||
.trim()
|
|
||||||
|
val links = brandmateDiv.select("a.ahref_Brand")
|
||||||
|
for (link in links) {
|
||||||
|
val persianName = link.text().trim()
|
||||||
|
val detailUrl = "https://www.darooyab.ir${link.attr("href")}"
|
||||||
|
|
||||||
|
forms.add(
|
||||||
|
DosageForm(
|
||||||
|
code = "",
|
||||||
|
persianName = persianName,
|
||||||
|
englishName = "",
|
||||||
|
isHighRisk = false,
|
||||||
|
temperature = null,
|
||||||
|
isVital = false,
|
||||||
|
warningLabel = null,
|
||||||
|
detailUrl = detailUrl
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return forms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Common Extractors (for both page types)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
private fun extractPregnancyCategory(document: Document): String? {
|
private fun extractPregnancyCategory(document: Document): String? {
|
||||||
|
// On generic pages only
|
||||||
val categoryElement = document.select("#UseInPregnancy .EnglishNumericFont, #UseInPregnancy > div.EnglishNumericFont").first()
|
val categoryElement = document.select("#UseInPregnancy .EnglishNumericFont, #UseInPregnancy > div.EnglishNumericFont").first()
|
||||||
return categoryElement?.text()?.trim()
|
return categoryElement?.text()?.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractPregnancyDescription(document: Document): String? {
|
private fun extractPregnancyDescription(document: Document): String? {
|
||||||
|
// On generic pages only
|
||||||
val descElement = document.select("#UseInPregnancy p, #UseInPregnancy .alert").first()
|
val descElement = document.select("#UseInPregnancy p, #UseInPregnancy .alert").first()
|
||||||
val text = descElement?.text()?.trim()
|
val text = descElement?.text()?.trim()
|
||||||
// اگر متن "مصرف در بارداری ثبت نشده است" باشد، null برگردان
|
|
||||||
return if (text.isNullOrBlank() || text.contains("ثبت نشده")) null else text
|
return if (text.isNullOrBlank() || text.contains("ثبت نشده")) null else text
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractDosageForms(document: Document): List<DosageForm> {
|
private fun extractDosageForms(document: Document): List<DosageForm> {
|
||||||
val forms = mutableListOf<DosageForm>()
|
val forms = mutableListOf<DosageForm>()
|
||||||
|
val table = document.select("#TBL_AshkalDarooyi").first() ?: return forms
|
||||||
// بررسی وجود جدول اشکال دارویی
|
|
||||||
val table = document.select("#TBL_AshkalDarooyi").first()
|
|
||||||
if (table == null) {
|
|
||||||
println("No dosage forms table found")
|
|
||||||
return forms
|
|
||||||
}
|
|
||||||
|
|
||||||
val rows = table.select("tbody tr").filter { !it.hasClass("showMoreRow") && it.id() != "showMoreRow" }
|
val rows = table.select("tbody tr").filter { !it.hasClass("showMoreRow") && it.id() != "showMoreRow" }
|
||||||
|
|
||||||
@@ -178,17 +372,13 @@ class DrugDetailParser {
|
|||||||
val cells = row.select("td")
|
val cells = row.select("td")
|
||||||
if (cells.size >= 2) {
|
if (cells.size >= 2) {
|
||||||
val persianNameElement = cells[1].select("h3").first()
|
val persianNameElement = cells[1].select("h3").first()
|
||||||
val englishNameElement = cells[1].select("label.EnglishNumericFont").first()
|
val persianName = persianNameElement?.text()?.trim() ?: continue
|
||||||
|
|
||||||
// بررسی اینکه آیا داده معتبر است
|
|
||||||
val persianName = persianNameElement?.text()?.trim()
|
|
||||||
if (persianName.isNullOrBlank()) continue
|
|
||||||
|
|
||||||
forms.add(
|
forms.add(
|
||||||
DosageForm(
|
DosageForm(
|
||||||
code = cells[0].text().trim(),
|
code = cells[0].text().trim(),
|
||||||
persianName = persianName,
|
persianName = persianName,
|
||||||
englishName = englishNameElement?.text()?.trim() ?: "",
|
englishName = cells[1].select("label.EnglishNumericFont").first()?.text()?.trim() ?: "",
|
||||||
isHighRisk = cells.getOrNull(2)?.hasText() == true,
|
isHighRisk = cells.getOrNull(2)?.hasText() == true,
|
||||||
temperature = cells.getOrNull(3)?.text()?.takeIf { it.isNotBlank() },
|
temperature = cells.getOrNull(3)?.text()?.takeIf { it.isNotBlank() },
|
||||||
isVital = cells.getOrNull(4)?.hasText() == true,
|
isVital = cells.getOrNull(4)?.hasText() == true,
|
||||||
@@ -200,34 +390,22 @@ class DrugDetailParser {
|
|||||||
println("Error parsing dosage form: ${e.message}")
|
println("Error parsing dosage form: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return forms
|
return forms
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractBrandNames(document: Document): List<BrandName> {
|
private fun extractBrandNames(document: Document): List<BrandName> {
|
||||||
val brands = mutableListOf<BrandName>()
|
val brands = mutableListOf<BrandName>()
|
||||||
|
|
||||||
// استخراج از بخش اسامی تجاری فارسی
|
|
||||||
val persianRows = document.select("#PersCommertialDrugs .tableCommertial tbody tr.tr_persian")
|
val persianRows = document.select("#PersCommertialDrugs .tableCommertial tbody tr.tr_persian")
|
||||||
|
|
||||||
// اگر ردیفی وجود نداشت، بررسی کن که آیا پیام "ثبت نشده" وجود دارد
|
if (persianRows.isEmpty()) return brands
|
||||||
if (persianRows.isEmpty()) {
|
|
||||||
val noDataMessage = document.select("#PersCommertialDrugs .alert")
|
|
||||||
if (noDataMessage.isNotEmpty()) {
|
|
||||||
println("No brand names available: ${noDataMessage.text()}")
|
|
||||||
}
|
|
||||||
return brands
|
|
||||||
}
|
|
||||||
|
|
||||||
for (row in persianRows) {
|
for (row in persianRows) {
|
||||||
try {
|
try {
|
||||||
val linkElement = row.select("td:first-child a.ahref_Generic").first()
|
val linkElement = row.select("td:first-child a.ahref_Generic").first()
|
||||||
val persianName = linkElement?.text()?.trim() ?: continue
|
val persianName = linkElement?.text()?.trim() ?: continue
|
||||||
val detailUrl = "https://www.darooyab.ir${linkElement.attr("href")}"
|
val detailUrl = "https://www.darooyab.ir${linkElement.attr("href")}"
|
||||||
|
|
||||||
val manufacturerElement = row.select("td:eq(1) a.ahref_Generic").first()
|
val manufacturerElement = row.select("td:eq(1) a.ahref_Generic").first()
|
||||||
val manufacturer = manufacturerElement?.text()?.trim()
|
val manufacturer = manufacturerElement?.text()?.trim()
|
||||||
|
|
||||||
val importerElement = row.select("td:eq(2) a.ahref_Generic").first()
|
val importerElement = row.select("td:eq(2) a.ahref_Generic").first()
|
||||||
val importer = importerElement?.text()?.trim()
|
val importer = importerElement?.text()?.trim()
|
||||||
|
|
||||||
@@ -244,19 +422,12 @@ class DrugDetailParser {
|
|||||||
println("Error parsing brand name: ${e.message}")
|
println("Error parsing brand name: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return brands
|
return brands
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractSimilarDrugs(document: Document): List<SimilarDrug> {
|
private fun extractSimilarDrugs(document: Document): List<SimilarDrug> {
|
||||||
val drugs = mutableListOf<SimilarDrug>()
|
val drugs = mutableListOf<SimilarDrug>()
|
||||||
|
val table = document.select("table.tableGroups").first() ?: return drugs
|
||||||
// بررسی وجود جدول داروهای هم گروه
|
|
||||||
val table = document.select("table.tableGroups").first()
|
|
||||||
if (table == null) {
|
|
||||||
println("No similar drugs table found")
|
|
||||||
return drugs
|
|
||||||
}
|
|
||||||
|
|
||||||
val rows = table.select("tbody tr").filter {
|
val rows = table.select("tbody tr").filter {
|
||||||
!it.hasClass("hidden-row") && it.select("a#toggleButton").isEmpty()
|
!it.hasClass("hidden-row") && it.select("a#toggleButton").isEmpty()
|
||||||
@@ -264,8 +435,7 @@ class DrugDetailParser {
|
|||||||
|
|
||||||
for (row in rows) {
|
for (row in rows) {
|
||||||
try {
|
try {
|
||||||
val cells = row.select("td")
|
for (cell in row.select("td")) {
|
||||||
for (cell in cells) {
|
|
||||||
val link = cell.select("a.ahref_Generic").first()
|
val link = cell.select("a.ahref_Generic").first()
|
||||||
if (link != null && link.text().isNotBlank()) {
|
if (link != null && link.text().isNotBlank()) {
|
||||||
val href = link.attr("href")
|
val href = link.attr("href")
|
||||||
@@ -286,7 +456,6 @@ class DrugDetailParser {
|
|||||||
println("Error parsing similar drug: ${e.message}")
|
println("Error parsing similar drug: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return drugs
|
return drugs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +464,6 @@ class DrugDetailParser {
|
|||||||
val martindale = martindaleLink?.text()?.trim()
|
val martindale = martindaleLink?.text()?.trim()
|
||||||
val martindaleUrl = martindaleLink?.attr("href")?.let { "https://www.darooyab.ir$it" }
|
val martindaleUrl = martindaleLink?.attr("href")?.let { "https://www.darooyab.ir$it" }
|
||||||
|
|
||||||
// بررسی طبقه بندی درمانی (ممکن است "بدون طبقه بندی درمانی" باشد)
|
|
||||||
val therapeuticLinks = document.select("#divExtraInfo > div:last-child a.ahref_Generic")
|
val therapeuticLinks = document.select("#divExtraInfo > div:last-child a.ahref_Generic")
|
||||||
val therapeutic = therapeuticLinks.mapNotNull { it.text().trim().takeIf { text ->
|
val therapeutic = therapeuticLinks.mapNotNull { it.text().trim().takeIf { text ->
|
||||||
text != "بدون طبقه بندی درمانی" && text.isNotBlank()
|
text != "بدون طبقه بندی درمانی" && text.isNotBlank()
|
||||||
@@ -320,15 +488,12 @@ class DrugDetailParser {
|
|||||||
try {
|
try {
|
||||||
val authorElement = element.select("span").first()
|
val authorElement = element.select("span").first()
|
||||||
val author = authorElement?.text()?.replace("(", "")?.replace(")", "")?.trim() ?: "ناشناس"
|
val author = authorElement?.text()?.replace("(", "")?.replace(")", "")?.trim() ?: "ناشناس"
|
||||||
|
|
||||||
val date = authorElement?.text()?.let {
|
val date = authorElement?.text()?.let {
|
||||||
val regex = "\\((\\d{4}/\\d{1,2}/\\d{1,2})\\)".toRegex()
|
val regex = "\\((\\d{4}/\\d{1,2}/\\d{1,2})\\)".toRegex()
|
||||||
regex.find(it)?.groupValues?.get(1) ?: ""
|
regex.find(it)?.groupValues?.get(1) ?: ""
|
||||||
} ?: ""
|
} ?: ""
|
||||||
|
|
||||||
val textElement = element.select("p.commentText").first()
|
val textElement = element.select("p.commentText").first()
|
||||||
val text = textElement?.text()?.trim() ?: ""
|
val text = textElement?.text()?.trim() ?: ""
|
||||||
|
|
||||||
if (text.isBlank()) continue
|
if (text.isBlank()) continue
|
||||||
|
|
||||||
val responseElement = element.select(".responseComment").first()
|
val responseElement = element.select(".responseComment").first()
|
||||||
@@ -346,7 +511,6 @@ class DrugDetailParser {
|
|||||||
println("Error parsing comment: ${e.message}")
|
println("Error parsing comment: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return comments
|
return comments
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,10 +518,8 @@ class DrugDetailParser {
|
|||||||
val doctorLink = element.select("a").first()
|
val doctorLink = element.select("a").first()
|
||||||
val doctorName = doctorLink?.text()?.trim() ?: ""
|
val doctorName = doctorLink?.text()?.trim() ?: ""
|
||||||
val doctorUrl = doctorLink?.attr("href")?.let { "https://www.darooyab.ir$it" }
|
val doctorUrl = doctorLink?.attr("href")?.let { "https://www.darooyab.ir$it" }
|
||||||
|
|
||||||
val doctorText = element.select("span").first()?.text()?.trim() ?: ""
|
val doctorText = element.select("span").first()?.text()?.trim() ?: ""
|
||||||
val doctorTitle = doctorText.substringAfter(" - ").takeIf { it.isNotBlank() } ?: ""
|
val doctorTitle = doctorText.substringAfter(" - ").takeIf { it.isNotBlank() } ?: ""
|
||||||
|
|
||||||
val responseText = element.select("p.commentText").last()?.text()?.trim() ?: ""
|
val responseText = element.select("p.commentText").last()?.text()?.trim() ?: ""
|
||||||
|
|
||||||
return CommentResponse(
|
return CommentResponse(
|
||||||
@@ -367,4 +529,29 @@ class DrugDetailParser {
|
|||||||
doctorUrl = doctorUrl
|
doctorUrl = doctorUrl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to clean and normalize text from an HTML element.
|
||||||
|
* Converts block elements to newlines for better readability.
|
||||||
|
*/
|
||||||
|
private fun cleanText(element: Element): String {
|
||||||
|
// Clone to avoid affecting the original
|
||||||
|
val clone = element.clone()
|
||||||
|
|
||||||
|
// Replace block-level elements with newlines for structure
|
||||||
|
clone.select("p, div, h2, h3, h4, li, br").before("\n")
|
||||||
|
|
||||||
|
// Get the text and clean it up
|
||||||
|
var text = clone.text()
|
||||||
|
.replace(Regex("\\n\\s*\\n+"), "\n\n") // Collapse multiple newlines
|
||||||
|
.replace(Regex("[ \\t]+"), " ") // Collapse spaces/tabs
|
||||||
|
.trim()
|
||||||
|
|
||||||
|
// Ensure lists look decent
|
||||||
|
if (element.tagName() == "li") {
|
||||||
|
text = "• $text"
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -20,9 +20,19 @@ data class DrugDetail(
|
|||||||
val brandNames: List<BrandName>,
|
val brandNames: List<BrandName>,
|
||||||
val similarDrugs: List<SimilarDrug>,
|
val similarDrugs: List<SimilarDrug>,
|
||||||
val categories: DrugCategories?,
|
val categories: DrugCategories?,
|
||||||
val comments: List<Comment>
|
val comments: List<Comment>,
|
||||||
|
val manufacturer: String? = null,
|
||||||
|
val genericInfo: GenericInfo? = null,
|
||||||
|
val otherBrandForms: List<DosageForm> = emptyList(),
|
||||||
|
val isGeneric: Boolean = true,
|
||||||
|
val generalInfo: String? = null,
|
||||||
|
val specializedInfo: String? = null,
|
||||||
|
)
|
||||||
|
data class GenericInfo(
|
||||||
|
val genericId: String,
|
||||||
|
val persianName: String,
|
||||||
|
val detailUrl: String
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DosageForm(
|
data class DosageForm(
|
||||||
val code: String,
|
val code: String,
|
||||||
val persianName: String,
|
val persianName: String,
|
||||||
@@ -30,7 +40,8 @@ data class DosageForm(
|
|||||||
val isHighRisk: Boolean,
|
val isHighRisk: Boolean,
|
||||||
val temperature: String?,
|
val temperature: String?,
|
||||||
val isVital: Boolean,
|
val isVital: Boolean,
|
||||||
val warningLabel: String?
|
val warningLabel: String?,
|
||||||
|
val detailUrl: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class BrandName(
|
data class BrandName(
|
||||||
|
|||||||
@@ -12,12 +12,10 @@ 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.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.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Favorite
|
import androidx.compose.material.icons.filled.Info
|
||||||
import androidx.compose.material.icons.filled.FavoriteBorder
|
|
||||||
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
|
||||||
@@ -36,7 +34,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
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
|
||||||
@@ -66,6 +63,7 @@ fun DrugDetailScreen(
|
|||||||
val dime = LocalDime.current
|
val dime = LocalDime.current
|
||||||
val detailState by viewModel.detailState.collectAsStateWithLifecycle()
|
val detailState by viewModel.detailState.collectAsStateWithLifecycle()
|
||||||
var selectedTab by remember { mutableIntStateOf(0) }
|
var selectedTab by remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
val tabs = listOf("عمومی", "تخصصی", "اشکال دارویی", "اسامی تجاری")
|
val tabs = listOf("عمومی", "تخصصی", "اشکال دارویی", "اسامی تجاری")
|
||||||
|
|
||||||
LaunchedEffect(detailUrl) {
|
LaunchedEffect(detailUrl) {
|
||||||
@@ -80,16 +78,16 @@ fun DrugDetailScreen(
|
|||||||
is DrugDetailYabState.Success -> {
|
is DrugDetailYabState.Success -> {
|
||||||
Column {
|
Column {
|
||||||
Text(
|
Text(
|
||||||
text = state.drugDetail.persianName.take(20),
|
text = state.drugDetail.persianName.take(25),
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
maxLines = 1
|
maxLines = 1
|
||||||
)
|
)
|
||||||
if (state.drugDetail.englishName.isNotEmpty()) {
|
if (state.drugDetail.englishName.isNotEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
text = state.drugDetail.englishName.take(25),
|
text = state.drugDetail.englishName.take(30),
|
||||||
fontSize = 12.sp,
|
fontSize = 11.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||||
maxLines = 1
|
maxLines = 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -120,9 +118,7 @@ fun DrugDetailScreen(
|
|||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
) {
|
) {
|
||||||
when (val state = detailState) {
|
when (val state = detailState) {
|
||||||
DrugDetailYabState.Idle -> {
|
DrugDetailYabState.Idle -> {}
|
||||||
// Initial state
|
|
||||||
}
|
|
||||||
|
|
||||||
DrugDetailYabState.Loading -> {
|
DrugDetailYabState.Loading -> {
|
||||||
Box(
|
Box(
|
||||||
@@ -144,9 +140,18 @@ fun DrugDetailScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
is DrugDetailYabState.Success -> {
|
is DrugDetailYabState.Success -> {
|
||||||
Column(
|
val drugDetail = state.drugDetail
|
||||||
modifier = Modifier.fillMaxSize()
|
|
||||||
) {
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
|
// Manufacturer Info Card (برای صفحات برند)
|
||||||
|
if (!drugDetail.isGeneric && drugDetail.manufacturer != null) {
|
||||||
|
ManufacturerInfoCard(
|
||||||
|
manufacturer = drugDetail.manufacturer!!,
|
||||||
|
genericInfo = drugDetail.genericInfo,
|
||||||
|
modifier = Modifier.padding(horizontal = dime.md, vertical = dime.sm)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Tab Row
|
// Tab Row
|
||||||
TabRow(
|
TabRow(
|
||||||
selectedTabIndex = selectedTab,
|
selectedTabIndex = selectedTab,
|
||||||
@@ -160,8 +165,9 @@ fun DrugDetailScreen(
|
|||||||
text = {
|
text = {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
fontSize = 14.sp,
|
fontSize = 13.sp,
|
||||||
fontWeight = if (selectedTab == index) FontWeight.Bold else FontWeight.Normal
|
fontWeight = if (selectedTab == index) FontWeight.Bold else FontWeight.Normal,
|
||||||
|
maxLines = 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -178,16 +184,16 @@ fun DrugDetailScreen(
|
|||||||
) {
|
) {
|
||||||
when (selectedTab) {
|
when (selectedTab) {
|
||||||
0 -> {
|
0 -> {
|
||||||
item { GeneralInfoTabContent(state.drugDetail) }
|
item { GeneralInfoTabContent(drugDetail) }
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
item { SpecializedInfoTabContent(state.drugDetail) }
|
item { SpecializedInfoTabContent(drugDetail) }
|
||||||
}
|
}
|
||||||
2 -> {
|
2 -> {
|
||||||
item { DosageFormsTabContent(state.drugDetail.dosageForms) }
|
item { DosageFormsTabContent(drugDetail) }
|
||||||
}
|
}
|
||||||
3 -> {
|
3 -> {
|
||||||
item { BrandNamesTabContent(state.drugDetail.brandNames) }
|
item { BrandNamesTabContent(drugDetail) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,117 +217,286 @@ fun DrugDetailScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GeneralInfoTabContent(drugDetail: DrugDetail) {
|
fun ManufacturerInfoCard(
|
||||||
|
manufacturer: String,
|
||||||
|
genericInfo: com.approagency.drug.domain.model.GenericInfo?,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
val dime = LocalDime.current
|
val dime = LocalDime.current
|
||||||
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(dime.md)) {
|
Card(
|
||||||
// Categories Card
|
modifier = modifier.fillMaxWidth(),
|
||||||
if (drugDetail.drugClass != null || drugDetail.therapeuticClass != null) {
|
colors = CardDefaults.cardColors(
|
||||||
Card(
|
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.15f)
|
||||||
modifier = Modifier.fillMaxWidth(),
|
),
|
||||||
colors = CardDefaults.cardColors(
|
shape = RoundedCornerShape(dime.sm)
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.2f)
|
) {
|
||||||
),
|
Column(
|
||||||
shape = RoundedCornerShape(dime.sm)
|
modifier = Modifier.padding(dime.md),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(dime.xs)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(dime.xs)
|
||||||
) {
|
) {
|
||||||
Column(
|
Icon(
|
||||||
modifier = Modifier.padding(dime.md),
|
imageVector = Icons.Default.Info,
|
||||||
verticalArrangement = Arrangement.spacedBy(dime.xs)
|
contentDescription = null,
|
||||||
) {
|
modifier = Modifier.size(18.dp),
|
||||||
drugDetail.drugClass?.let {
|
tint = MaterialTheme.colorScheme.primary
|
||||||
Row {
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "طبقه بندی مارتیندل: ",
|
text = "سازنده:",
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 14.sp
|
fontSize = 13.sp
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = it,
|
text = manufacturer,
|
||||||
fontSize = 14.sp,
|
fontSize = 13.sp,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
drugDetail.therapeuticClass?.let {
|
|
||||||
Row {
|
|
||||||
Text(
|
|
||||||
text = "طبقه درمانی: ",
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
fontSize = 14.sp
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = it,
|
|
||||||
fontSize = 14.sp,
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Pregnancy Card
|
if (genericInfo != null) {
|
||||||
if (drugDetail.pregnancyCategory != null || drugDetail.pregnancyDescription != null) {
|
Row(
|
||||||
Card(
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
horizontalArrangement = Arrangement.spacedBy(dime.xs)
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = Color(0xFFFFF3E0)
|
|
||||||
),
|
|
||||||
shape = RoundedCornerShape(dime.sm)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(dime.md),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(dime.xs)
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Icon(
|
||||||
text = "مصرف در بارداری",
|
imageVector = Icons.Default.Info,
|
||||||
fontWeight = FontWeight.Bold,
|
contentDescription = null,
|
||||||
fontSize = 16.sp,
|
modifier = Modifier.size(18.dp),
|
||||||
color = Color(0xFFE65100)
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "ماده موثره:",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 13.sp
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = genericInfo.persianName,
|
||||||
|
fontSize = 13.sp,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
drugDetail.pregnancyCategory?.let {
|
|
||||||
Text(
|
|
||||||
text = "گروه: $it",
|
|
||||||
fontSize = 14.sp,
|
|
||||||
fontWeight = FontWeight.Medium
|
|
||||||
)
|
|
||||||
}
|
|
||||||
drugDetail.pregnancyDescription?.let {
|
|
||||||
Text(
|
|
||||||
text = it,
|
|
||||||
fontSize = 13.sp,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usage Section
|
|
||||||
drugDetail.usage?.let {
|
|
||||||
InfoSectionCard(title = "موارد مصرف", content = it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contraindications Section
|
|
||||||
drugDetail.contraindications?.let {
|
|
||||||
InfoSectionCard(title = "موارد منع مصرف", content = it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Side Effects Section
|
|
||||||
drugDetail.sideEffects?.let {
|
|
||||||
InfoSectionCard(title = "عوارض جانبی", content = it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnings Section
|
|
||||||
drugDetail.warnings?.let {
|
|
||||||
InfoSectionCard(title = "هشدارها", content = it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// آپدیت GeneralInfoTabContent برای صفحات Generic
|
||||||
|
@Composable
|
||||||
|
fun GeneralInfoTabContent(drugDetail: DrugDetail) {
|
||||||
|
val dime = LocalDime.current
|
||||||
|
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(dime.md)) {
|
||||||
|
|
||||||
|
if (drugDetail.isGeneric) {
|
||||||
|
// ========== صفحات ژنریک (Generic) ==========
|
||||||
|
|
||||||
|
// اگر generalInfo وجود دارد (از EtelaatOmomiVaTakhasosi یا EtelaatOmomi)
|
||||||
|
if (!drugDetail.generalInfo.isNullOrBlank()) {
|
||||||
|
InfoSectionCard(
|
||||||
|
title = "اطلاعات عمومی",
|
||||||
|
content = drugDetail.generalInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// اگر specializedInfo وجود دارد (از EtelaatTakhasosiContent)
|
||||||
|
else if (!drugDetail.specializedInfo.isNullOrBlank()) {
|
||||||
|
InfoSectionCard(
|
||||||
|
title = "اطلاعات تخصصی",
|
||||||
|
content = drugDetail.specializedInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// در غیر این صورت، اطلاعات پراکنده را نمایش بده
|
||||||
|
else {
|
||||||
|
// Categories Card
|
||||||
|
if (drugDetail.drugClass != null || drugDetail.therapeuticClass != null) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.2f)
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(dime.sm)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(dime.md),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(dime.xs)
|
||||||
|
) {
|
||||||
|
drugDetail.drugClass?.let {
|
||||||
|
Row {
|
||||||
|
Text(
|
||||||
|
text = "طبقه بندی مارتیندل: ",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 13.sp
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
fontSize = 13.sp,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drugDetail.therapeuticClass?.let {
|
||||||
|
Row {
|
||||||
|
Text(
|
||||||
|
text = "طبقه درمانی: ",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 13.sp
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
fontSize = 13.sp,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pregnancy Card
|
||||||
|
if (drugDetail.pregnancyCategory != null || drugDetail.pregnancyDescription != null) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = Color(0xFFFFF3E0)
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(dime.sm)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(dime.md),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(dime.xs)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "مصرف در بارداری",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 15.sp,
|
||||||
|
color = Color(0xFFE65100)
|
||||||
|
)
|
||||||
|
drugDetail.pregnancyCategory?.let {
|
||||||
|
Text(
|
||||||
|
text = "گروه: $it",
|
||||||
|
fontSize = 13.sp,
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
drugDetail.pregnancyDescription?.let {
|
||||||
|
Spacer(modifier = Modifier.height(dime.xs))
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
lineHeight = 18.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// موارد مصرف
|
||||||
|
drugDetail.usage?.let {
|
||||||
|
InfoSectionCard(title = "موارد مصرف", content = it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// موارد منع مصرف
|
||||||
|
drugDetail.contraindications?.let {
|
||||||
|
InfoSectionCard(title = "موارد منع مصرف", content = it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// عوارض جانبی
|
||||||
|
drugDetail.sideEffects?.let {
|
||||||
|
InfoSectionCard(title = "عوارض جانبی", content = it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// هشدارها
|
||||||
|
drugDetail.warnings?.let {
|
||||||
|
InfoSectionCard(title = "هشدارها", content = it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// توصیههای دارویی
|
||||||
|
drugDetail.recommendations?.let {
|
||||||
|
InfoSectionCard(title = "توصیههای دارویی", content = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// اگر هیچ اطلاعاتی وجود نداشت
|
||||||
|
if (drugDetail.drugClass == null &&
|
||||||
|
drugDetail.therapeuticClass == null &&
|
||||||
|
drugDetail.pregnancyCategory == null &&
|
||||||
|
drugDetail.usage == null &&
|
||||||
|
drugDetail.contraindications == null &&
|
||||||
|
drugDetail.sideEffects == null &&
|
||||||
|
drugDetail.warnings == null &&
|
||||||
|
drugDetail.generalInfo.isNullOrBlank() &&
|
||||||
|
drugDetail.specializedInfo.isNullOrBlank()
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(32.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "اطلاعات عمومی برای این دارو ثبت نشده است",
|
||||||
|
fontSize = 13.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ========== صفحات برند (Brand) ==========
|
||||||
|
// نمایش اطلاعات تخصصی برند که در بخش brandAttrPersDesc است
|
||||||
|
drugDetail.usage?.let {
|
||||||
|
InfoSectionCard(title = "موارد مصرف", content = it)
|
||||||
|
}
|
||||||
|
drugDetail.contraindications?.let {
|
||||||
|
InfoSectionCard(title = "موارد منع مصرف", content = it)
|
||||||
|
}
|
||||||
|
drugDetail.sideEffects?.let {
|
||||||
|
InfoSectionCard(title = "عوارض جانبی", content = it)
|
||||||
|
}
|
||||||
|
drugDetail.warnings?.let {
|
||||||
|
InfoSectionCard(title = "هشدارها", content = it)
|
||||||
|
}
|
||||||
|
drugDetail.recommendations?.let {
|
||||||
|
InfoSectionCard(title = "توصیههای دارویی", content = it)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drugDetail.usage == null &&
|
||||||
|
drugDetail.contraindications == null &&
|
||||||
|
drugDetail.sideEffects == null &&
|
||||||
|
drugDetail.warnings == null
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(32.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "اطلاعات عمومی برای این برند ثبت نشده است.\nبرای مشاهده اطلاعات کامل، به صفحه داروی ژنریک مراجعه کنید.",
|
||||||
|
fontSize = 13.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpecializedInfoTabContent(drugDetail: DrugDetail) {
|
fun SpecializedInfoTabContent(drugDetail: DrugDetail) {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
val dime = LocalDime.current
|
||||||
|
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(dime.md)) {
|
||||||
|
|
||||||
|
// برای صفحات برند، اطلاعات تخصصی معمولاً در قسمت brandAttrPersDesc است
|
||||||
|
// که در پارسر ما به صورت usage, contraindications, sideEffects, warnings, recommendations ذخیره شده
|
||||||
|
|
||||||
drugDetail.usage?.let {
|
drugDetail.usage?.let {
|
||||||
InfoSectionCard(title = "موارد مصرف", content = it)
|
InfoSectionCard(title = "موارد مصرف", content = it)
|
||||||
}
|
}
|
||||||
@@ -347,10 +522,12 @@ fun SpecializedInfoTabContent(drugDetail: DrugDetail) {
|
|||||||
InfoSectionCard(title = "توصیههای دارویی", content = it)
|
InfoSectionCard(title = "توصیههای دارویی", content = it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// اگر هیچ اطلاعات تخصصی وجود نداشت
|
||||||
if (drugDetail.usage == null &&
|
if (drugDetail.usage == null &&
|
||||||
drugDetail.mechanism == null &&
|
drugDetail.mechanism == null &&
|
||||||
drugDetail.contraindications == null &&
|
drugDetail.contraindications == null &&
|
||||||
drugDetail.sideEffects == null
|
drugDetail.sideEffects == null &&
|
||||||
|
drugDetail.warnings == null
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -359,8 +536,11 @@ fun SpecializedInfoTabContent(drugDetail: DrugDetail) {
|
|||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "اطلاعات تخصصی برای این دارو ثبت نشده است",
|
text = if (drugDetail.isGeneric)
|
||||||
fontSize = 14.sp,
|
"اطلاعات تخصصی برای این دارو ثبت نشده است"
|
||||||
|
else
|
||||||
|
"اطلاعات تخصصی برای این برند ثبت نشده است.\nبرای مشاهده اطلاعات کامل، به صفحه داروی ژنریک مراجعه کنید.",
|
||||||
|
fontSize = 13.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
@@ -370,8 +550,9 @@ fun SpecializedInfoTabContent(drugDetail: DrugDetail) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DosageFormsTabContent(dosageForms: List<com.approagency.drug.domain.model.DosageForm>) {
|
fun DosageFormsTabContent(drugDetail: DrugDetail) {
|
||||||
val dime = LocalDime.current
|
val dime = LocalDime.current
|
||||||
|
val dosageForms = drugDetail.dosageForms
|
||||||
|
|
||||||
if (dosageForms.isEmpty()) {
|
if (dosageForms.isEmpty()) {
|
||||||
Box(
|
Box(
|
||||||
@@ -381,18 +562,32 @@ fun DosageFormsTabContent(dosageForms: List<com.approagency.drug.domain.model.Do
|
|||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "اشکال دارویی برای این دارو ثبت نشده است",
|
text = if (drugDetail.isGeneric)
|
||||||
fontSize = 14.sp,
|
"اشکال دارویی برای این دارو ثبت نشده است"
|
||||||
|
else
|
||||||
|
"سایر اشکال دارویی این برند ثبت نشده است",
|
||||||
|
fontSize = 13.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(dime.sm)) {
|
Column(verticalArrangement = Arrangement.spacedBy(dime.sm)) {
|
||||||
|
// اگر صفحه برند است، عنوان متفاوت نشان بده
|
||||||
|
if (!drugDetail.isGeneric) {
|
||||||
|
Text(
|
||||||
|
text = "سایر محصولات این برند",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.padding(bottom = dime.xs)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
dosageForms.forEach { form ->
|
dosageForms.forEach { form ->
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
|
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp),
|
||||||
shape = RoundedCornerShape(dime.sm)
|
shape = RoundedCornerShape(dime.sm)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@@ -401,55 +596,49 @@ fun DosageFormsTabContent(dosageForms: List<com.approagency.drug.domain.model.Do
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = form.persianName,
|
text = form.persianName,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = 15.sp
|
fontSize = 14.sp
|
||||||
)
|
)
|
||||||
if (form.englishName.isNotBlank()) {
|
if (form.englishName.isNotBlank()) {
|
||||||
Text(
|
Text(
|
||||||
text = form.englishName,
|
text = form.englishName,
|
||||||
fontSize = 12.sp,
|
fontSize = 11.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(dime.sm)
|
// نشانگرهای خطر
|
||||||
) {
|
if (form.isHighRisk || form.isVital) {
|
||||||
if (form.isHighRisk) {
|
Row(
|
||||||
androidx.compose.material3.Surface(
|
horizontalArrangement = Arrangement.spacedBy(dime.xs)
|
||||||
shape = RoundedCornerShape(4.dp),
|
) {
|
||||||
color = Color(0xFFFFEBEE),
|
if (form.isHighRisk) {
|
||||||
modifier = Modifier.padding(vertical = 4.dp)
|
androidx.compose.material3.Surface(
|
||||||
) {
|
shape = RoundedCornerShape(4.dp),
|
||||||
Text(
|
color = Color(0xFFFFEBEE)
|
||||||
text = "پرخطر",
|
) {
|
||||||
fontSize = 10.sp,
|
Text(
|
||||||
color = Color(0xFFC62828),
|
text = "پرخطر",
|
||||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)
|
fontSize = 10.sp,
|
||||||
)
|
color = Color(0xFFC62828),
|
||||||
|
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (form.isVital) {
|
||||||
|
androidx.compose.material3.Surface(
|
||||||
|
shape = RoundedCornerShape(4.dp),
|
||||||
|
color = Color(0xFFE8F5E9)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "حیاتی",
|
||||||
|
fontSize = 10.sp,
|
||||||
|
color = Color(0xFF2E7D32),
|
||||||
|
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (form.isVital) {
|
|
||||||
androidx.compose.material3.Surface(
|
|
||||||
shape = RoundedCornerShape(4.dp),
|
|
||||||
color = Color(0xFFE8F5E9),
|
|
||||||
modifier = Modifier.padding(vertical = 4.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "حیاتی",
|
|
||||||
fontSize = 10.sp,
|
|
||||||
color = Color(0xFF2E7D32),
|
|
||||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!form.warningLabel.isNullOrBlank()) {
|
|
||||||
Text(
|
|
||||||
text = "⚠️ ${form.warningLabel}",
|
|
||||||
fontSize = 11.sp,
|
|
||||||
color = Color(0xFFE65100),
|
|
||||||
modifier = Modifier.padding(top = dime.xs)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,10 +648,10 @@ fun DosageFormsTabContent(dosageForms: List<com.approagency.drug.domain.model.Do
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BrandNamesTabContent(brandNames: List<com.approagency.drug.domain.model.BrandName>) {
|
fun BrandNamesTabContent(drugDetail: DrugDetail) {
|
||||||
val dime = LocalDime.current
|
val dime = LocalDime.current
|
||||||
|
|
||||||
if (brandNames.isEmpty()) {
|
if (drugDetail.isGeneric && drugDetail.brandNames.isEmpty()) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -471,14 +660,14 @@ fun BrandNamesTabContent(brandNames: List<com.approagency.drug.domain.model.Bran
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "اسامی تجاری برای این دارو ثبت نشده است",
|
text = "اسامی تجاری برای این دارو ثبت نشده است",
|
||||||
fontSize = 14.sp,
|
fontSize = 13.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else if (drugDetail.isGeneric && drugDetail.brandNames.isNotEmpty()) {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(dime.sm)) {
|
Column(verticalArrangement = Arrangement.spacedBy(dime.sm)) {
|
||||||
brandNames.forEach { brand ->
|
drugDetail.brandNames.forEach { brand ->
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp),
|
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp),
|
||||||
@@ -525,6 +714,21 @@ fun BrandNamesTabContent(brandNames: List<com.approagency.drug.domain.model.Bran
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// صفحه برند است - اسامی تجاری برای برند معنی ندارد
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(32.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "این صفحه مربوط به یک محصول تجاری است. برای مشاهده اسامی تجاری سایر برندها، به صفحه داروی ژنریک مراجعه کنید.",
|
||||||
|
fontSize = 13.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,14 +752,14 @@ fun InfoSectionCard(
|
|||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 16.sp,
|
fontSize = 15.sp,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
Divider(color = MaterialTheme.colorScheme.outline.copy(alpha = 0.2f))
|
Divider(color = MaterialTheme.colorScheme.outline.copy(alpha = 0.2f))
|
||||||
Text(
|
Text(
|
||||||
text = content,
|
text = content,
|
||||||
fontSize = 14.sp,
|
fontSize = 13.sp,
|
||||||
lineHeight = 22.sp,
|
lineHeight = 20.sp,
|
||||||
textAlign = TextAlign.Justify,
|
textAlign = TextAlign.Justify,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.85f)
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.85f)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user