This commit is contained in:
2026-06-07 23:53:51 +03:30
parent d1718793d9
commit 0ae8fa19bc
4 changed files with 373 additions and 384 deletions
@@ -1,25 +1,31 @@
package com.approagency.pharmacy.presentation.common package com.approagency.pharmacy.presentation.common
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
@Composable @Composable
fun CustomBox( fun CustomBox(
modifier: Modifier? = Modifier, modifier: Modifier = Modifier,
child: @Composable ()-> Unit child: @Composable () -> Unit
) { ) {
if (modifier != null) { val shape = MaterialTheme.shapes.large
Box( Box(
modifier = modifier.clip(shape = MaterialTheme.shapes.large).background( modifier = modifier
color = MaterialTheme.colorScheme.surfaceContainer .clip(shape)
.background(color = MaterialTheme.colorScheme.surfaceContainerLowest)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.outlineVariant,
shape = shape
) )
) { ) {
child() child()
}
} }
} }
@@ -1,8 +1,12 @@
package com.approagency.pharmacy.presentation.screens package com.approagency.pharmacy.presentation.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -15,9 +19,6 @@ 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.Info import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -39,8 +40,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@@ -52,6 +55,7 @@ import com.approagency.pharmacy.presentation.common.Loading
import com.approagency.pharmacy.presentation.viewModel.DrugDetailViewModel import com.approagency.pharmacy.presentation.viewModel.DrugDetailViewModel
import com.approagency.pharmacy.presentation.viewModel.DrugDetailYabState import com.approagency.pharmacy.presentation.viewModel.DrugDetailYabState
import com.vada.caller.ui.theme.LocalDime import com.vada.caller.ui.theme.LocalDime
import com.vada.caller.ui.theme.dime
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -85,17 +89,21 @@ fun DrugDetailScreen(
) )
if (state.drugDetail.englishName.isNotEmpty()) { if (state.drugDetail.englishName.isNotEmpty()) {
Text( Text(
text = state.drugDetail.englishName.take(30), text = state.drugDetail.englishName,
fontSize = 11.sp, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f), color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1 maxLines = 1,
overflow = TextOverflow.Ellipsis
) )
} }
} }
} }
else -> { else -> {
Text("جزئیات دارو", fontSize = 16.sp) Text(
text = "جزئیات دارو",
style = MaterialTheme.typography.titleMedium
)
} }
} }
}, },
@@ -157,7 +165,7 @@ fun DrugDetailScreen(
text = { text = {
Text( Text(
text = title, text = title,
fontSize = 13.sp, style = MaterialTheme.typography.labelLarge,
fontWeight = if (selectedTab == index) FontWeight.Bold else FontWeight.Normal, fontWeight = if (selectedTab == index) FontWeight.Bold else FontWeight.Normal,
maxLines = 1 maxLines = 1
) )
@@ -170,8 +178,8 @@ fun DrugDetailScreen(
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(horizontal = dime.md) .padding(horizontal = dime.md),
.padding(top = dime.md), contentPadding = PaddingValues(top = dime.md, bottom = dime.xxl),
verticalArrangement = Arrangement.spacedBy(dime.md) verticalArrangement = Arrangement.spacedBy(dime.md)
) { ) {
when (selectedTab) { when (selectedTab) {
@@ -219,63 +227,60 @@ fun ManufacturerInfoCard(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val dime = LocalDime.current val dime = LocalDime.current
val onContainer = MaterialTheme.colorScheme.onSecondaryContainer
Card( Column(
modifier = modifier.fillMaxWidth(), modifier = modifier
colors = CardDefaults.cardColors( .fillMaxWidth()
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.15f) .clip(MaterialTheme.shapes.large)
), .background(MaterialTheme.colorScheme.secondaryContainer)
shape = RoundedCornerShape(dime.sm) .padding(dime.md),
verticalArrangement = Arrangement.spacedBy(dime.sm)
) { ) {
Column( Row(
modifier = Modifier.padding(dime.md), verticalAlignment = Alignment.CenterVertically,
verticalArrangement = Arrangement.spacedBy(dime.xs) horizontalArrangement = Arrangement.spacedBy(dime.sm)
) { ) {
Icon(
imageVector = Icons.Default.Info,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = onContainer
)
Text(
text = "سازنده:",
style = MaterialTheme.typography.labelLarge,
color = onContainer
)
Text(
text = manufacturer,
style = MaterialTheme.typography.bodyMedium,
color = onContainer
)
}
if (genericInfo != null) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dime.xs) horizontalArrangement = Arrangement.spacedBy(dime.sm)
) { ) {
Icon( Icon(
imageVector = Icons.Default.Info, imageVector = Icons.Default.Info,
contentDescription = null, contentDescription = null,
modifier = Modifier.size(18.dp), modifier = Modifier.size(18.dp),
tint = MaterialTheme.colorScheme.primary tint = onContainer
) )
Text( Text(
text = "سازنده:", text = "ماده موثره:",
fontWeight = FontWeight.Bold, style = MaterialTheme.typography.labelLarge,
fontSize = 13.sp color = onContainer
) )
Text( Text(
text = manufacturer, text = genericInfo.persianName,
fontSize = 13.sp, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary color = onContainer
) )
} }
if (genericInfo != null) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dime.xs)
) {
Icon(
imageVector = Icons.Default.Info,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = MaterialTheme.colorScheme.primary
)
Text(
text = "ماده موثره:",
fontWeight = FontWeight.Bold,
fontSize = 13.sp
)
Text(
text = genericInfo.persianName,
fontSize = 13.sp,
color = MaterialTheme.colorScheme.primary
)
}
}
} }
} }
} }
@@ -308,44 +313,13 @@ fun GeneralInfoTabContent(drugDetail: DrugDetail) {
else { else {
// Categories Card // Categories Card
if (drugDetail.drugClass != null || drugDetail.therapeuticClass != null) { if (drugDetail.drugClass != null || drugDetail.therapeuticClass != null) {
Card( DetailCard {
modifier = Modifier.fillMaxWidth(), Column(verticalArrangement = Arrangement.spacedBy(dime.xs)) {
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 { drugDetail.drugClass?.let {
Row { LabeledRow(label = "طبقه بندی مارتیندل", value = it)
Text(
text = "طبقه بندی مارتیندل: ",
fontWeight = FontWeight.Bold,
fontSize = 13.sp
)
Text(
text = it,
fontSize = 13.sp,
color = MaterialTheme.colorScheme.primary
)
}
} }
drugDetail.therapeuticClass?.let { drugDetail.therapeuticClass?.let {
Row { LabeledRow(label = "طبقه درمانی", value = it)
Text(
text = "طبقه درمانی: ",
fontWeight = FontWeight.Bold,
fontSize = 13.sp
)
Text(
text = it,
fontSize = 13.sp,
color = MaterialTheme.colorScheme.primary
)
}
} }
} }
} }
@@ -353,39 +327,36 @@ fun GeneralInfoTabContent(drugDetail: DrugDetail) {
// Pregnancy Card // Pregnancy Card
if (drugDetail.pregnancyCategory != null || drugDetail.pregnancyDescription != null) { if (drugDetail.pregnancyCategory != null || drugDetail.pregnancyDescription != null) {
Card( val onContainer = MaterialTheme.colorScheme.onTertiaryContainer
modifier = Modifier.fillMaxWidth(), Column(
colors = CardDefaults.cardColors( modifier = Modifier
containerColor = Color(0xFFFFF3E0) .fillMaxWidth()
), .clip(MaterialTheme.shapes.large)
shape = RoundedCornerShape(dime.sm) .background(MaterialTheme.colorScheme.tertiaryContainer)
.padding(dime.md),
verticalArrangement = Arrangement.spacedBy(dime.xs)
) { ) {
Column( Text(
modifier = Modifier.padding(dime.md), text = "مصرف در بارداری",
verticalArrangement = Arrangement.spacedBy(dime.xs) style = MaterialTheme.typography.titleSmall,
) { fontWeight = FontWeight.Bold,
color = onContainer
)
drugDetail.pregnancyCategory?.let {
Text( Text(
text = "مصرف در بارداری", text = "گروه: $it",
fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyMedium,
fontSize = 15.sp, fontWeight = FontWeight.Medium,
color = Color(0xFFE65100) color = onContainer
)
}
drugDetail.pregnancyDescription?.let {
Text(
text = it,
style = MaterialTheme.typography.bodySmall,
lineHeight = 18.sp,
color = onContainer.copy(alpha = 0.9f)
) )
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)
)
}
} }
} }
} }
@@ -427,19 +398,7 @@ fun GeneralInfoTabContent(drugDetail: DrugDetail) {
drugDetail.generalInfo.isNullOrBlank() && drugDetail.generalInfo.isNullOrBlank() &&
drugDetail.specializedInfo.isNullOrBlank() drugDetail.specializedInfo.isNullOrBlank()
) { ) {
Box( DetailEmptyState(text = "اطلاعات عمومی برای این دارو ثبت نشده است")
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 { } else {
// ========== صفحات برند (Brand) ========== // ========== صفحات برند (Brand) ==========
@@ -465,19 +424,9 @@ fun GeneralInfoTabContent(drugDetail: DrugDetail) {
drugDetail.sideEffects == null && drugDetail.sideEffects == null &&
drugDetail.warnings == null drugDetail.warnings == null
) { ) {
Box( DetailEmptyState(
modifier = Modifier text = "اطلاعات عمومی برای این برند ثبت نشده است.\nبرای مشاهده اطلاعات کامل، به صفحه داروی ژنریک مراجعه کنید."
.fillMaxWidth() )
.padding(32.dp),
contentAlignment = Alignment.Center
) {
Text(
text = "اطلاعات عمومی برای این برند ثبت نشده است.\nبرای مشاهده اطلاعات کامل، به صفحه داروی ژنریک مراجعه کنید.",
fontSize = 13.sp,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
textAlign = TextAlign.Center
)
}
} }
} }
} }
@@ -525,22 +474,12 @@ fun SpecializedInfoTabContent(drugDetail: DrugDetail) {
drugDetail.sideEffects == null && drugDetail.sideEffects == null &&
drugDetail.warnings == null drugDetail.warnings == null
) { ) {
Box( DetailEmptyState(
modifier = Modifier text = if (drugDetail.isGeneric)
.fillMaxWidth() "اطلاعات تخصصی برای این دارو ثبت نشده است"
.padding(32.dp), else
contentAlignment = Alignment.Center "اطلاعات تخصصی برای این برند ثبت نشده است.\nبرای مشاهده اطلاعات کامل، به صفحه داروی ژنریک مراجعه کنید."
) { )
Text(
text = if (drugDetail.isGeneric)
"اطلاعات تخصصی برای این دارو ثبت نشده است"
else
"اطلاعات تخصصی برای این برند ثبت نشده است.\nبرای مشاهده اطلاعات کامل، به صفحه داروی ژنریک مراجعه کنید.",
fontSize = 13.sp,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
textAlign = TextAlign.Center
)
}
} }
} }
} }
@@ -551,89 +490,54 @@ fun DosageFormsTabContent(drugDetail: DrugDetail) {
val dosageForms = drugDetail.dosageForms val dosageForms = drugDetail.dosageForms
if (dosageForms.isEmpty()) { if (dosageForms.isEmpty()) {
Box( DetailEmptyState(
modifier = Modifier text = if (drugDetail.isGeneric)
.fillMaxWidth() "اشکال دارویی برای این دارو ثبت نشده است"
.padding(32.dp), else
contentAlignment = Alignment.Center "سایر اشکال دارویی این برند ثبت نشده است"
) { )
Text(
text = if (drugDetail.isGeneric)
"اشکال دارویی برای این دارو ثبت نشده است"
else
"سایر اشکال دارویی این برند ثبت نشده است",
fontSize = 13.sp,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
textAlign = TextAlign.Center
)
}
} else { } else {
Column(verticalArrangement = Arrangement.spacedBy(dime.sm)) { Column(verticalArrangement = Arrangement.spacedBy(dime.sm)) {
// اگر صفحه برند است، عنوان متفاوت نشان بده // اگر صفحه برند است، عنوان متفاوت نشان بده
if (!drugDetail.isGeneric) { if (!drugDetail.isGeneric) {
Text( SectionHeader(
text = "سایر محصولات این برند", title = "سایر محصولات این برند",
fontWeight = FontWeight.Bold,
fontSize = 14.sp,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(bottom = dime.xs) modifier = Modifier.padding(bottom = dime.xs)
) )
} }
dosageForms.forEach { form -> dosageForms.forEach { form ->
Card( DetailCard {
modifier = Modifier.fillMaxWidth(), Text(
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp), text = form.persianName,
shape = RoundedCornerShape(dime.sm) style = MaterialTheme.typography.titleSmall
) { )
Column( if (form.englishName.isNotBlank()) {
modifier = Modifier.padding(dime.md), Spacer(modifier = Modifier.height(dime.xxs))
verticalArrangement = Arrangement.spacedBy(dime.xs)
) {
Text( Text(
text = form.persianName, text = form.englishName,
fontWeight = FontWeight.Medium, style = MaterialTheme.typography.bodySmall,
fontSize = 14.sp color = MaterialTheme.colorScheme.onSurfaceVariant
) )
if (form.englishName.isNotBlank()) { }
Text(
text = form.englishName,
fontSize = 11.sp,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
)
}
// نشانگرهای خطر // نشانگرهای خطر
if (form.isHighRisk || form.isVital) { if (form.isHighRisk || form.isVital) {
Row( Spacer(modifier = Modifier.height(dime.sm))
horizontalArrangement = Arrangement.spacedBy(dime.xs) Row(horizontalArrangement = Arrangement.spacedBy(dime.xs)) {
) { if (form.isHighRisk) {
if (form.isHighRisk) { TagChip(
androidx.compose.material3.Surface( text = "پرخطر",
shape = RoundedCornerShape(4.dp), container = MaterialTheme.colorScheme.errorContainer,
color = Color(0xFFFFEBEE) onContainer = MaterialTheme.colorScheme.onErrorContainer
) { )
Text( }
text = "پرخطر", if (form.isVital) {
fontSize = 10.sp, TagChip(
color = Color(0xFFC62828), text = "حیاتی",
modifier = Modifier.padding(horizontal = 6.dp, vertical = dime.xxs) container = MaterialTheme.colorScheme.primaryContainer,
) onContainer = MaterialTheme.colorScheme.onPrimaryContainer
} )
}
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 = dime.xxs)
)
}
}
} }
} }
} }
@@ -648,83 +552,31 @@ fun BrandNamesTabContent(drugDetail: DrugDetail) {
val dime = LocalDime.current val dime = LocalDime.current
if (drugDetail.isGeneric && drugDetail.brandNames.isEmpty()) { if (drugDetail.isGeneric && drugDetail.brandNames.isEmpty()) {
Box( DetailEmptyState(text = "اسامی تجاری برای این دارو ثبت نشده است")
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 if (drugDetail.isGeneric && drugDetail.brandNames.isNotEmpty()) { } else if (drugDetail.isGeneric && drugDetail.brandNames.isNotEmpty()) {
Column(verticalArrangement = Arrangement.spacedBy(dime.sm)) { Column(verticalArrangement = Arrangement.spacedBy(dime.sm)) {
drugDetail.brandNames.forEach { brand -> drugDetail.brandNames.forEach { brand ->
Card( DetailCard {
modifier = Modifier.fillMaxWidth(), Text(
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp), text = brand.persianName,
shape = RoundedCornerShape(dime.sm) style = MaterialTheme.typography.titleSmall
) { )
Column( brand.manufacturer?.let {
modifier = Modifier.padding(dime.md), Spacer(modifier = Modifier.height(dime.xs))
verticalArrangement = Arrangement.spacedBy(dime.xs) LabeledRow(label = "تولید کننده", value = it)
) { }
Text( brand.importer?.let {
text = brand.persianName, Spacer(modifier = Modifier.height(dime.xxs))
fontWeight = FontWeight.Medium, LabeledRow(label = "وارد کننده", value = it)
fontSize = 14.sp
)
brand.manufacturer?.let {
Row {
Text(
text = "تولید کننده: ",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
)
Text(
text = it,
fontSize = 12.sp,
color = MaterialTheme.colorScheme.primary
)
}
}
brand.importer?.let {
Row {
Text(
text = "وارد کننده: ",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
)
Text(
text = it,
fontSize = 12.sp,
color = MaterialTheme.colorScheme.primary
)
}
}
} }
} }
} }
} }
} else { } else {
// صفحه برند است - اسامی تجاری برای برند معنی ندارد // صفحه برند است - اسامی تجاری برای برند معنی ندارد
Box( DetailEmptyState(
modifier = Modifier text = "این صفحه مربوط به یک محصول تجاری است. برای مشاهده اسامی تجاری سایر برندها، به صفحه داروی ژنریک مراجعه کنید."
.fillMaxWidth() )
.padding(32.dp),
contentAlignment = Alignment.Center
) {
Text(
text = "این صفحه مربوط به یک محصول تجاری است. برای مشاهده اسامی تجاری سایر برندها، به صفحه داروی ژنریک مراجعه کنید.",
fontSize = 13.sp,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
textAlign = TextAlign.Center
)
}
} }
} }
@@ -736,29 +588,136 @@ fun InfoSectionCard(
) { ) {
val dime = LocalDime.current val dime = LocalDime.current
Card( DetailCard(modifier = modifier) {
modifier = modifier.fillMaxWidth(), SectionHeader(title = title)
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp), Spacer(modifier = Modifier.height(dime.sm))
shape = RoundedCornerShape(dime.sm) Text(
) { text = content,
Column( style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(dime.md), lineHeight = 22.sp,
verticalArrangement = Arrangement.spacedBy(dime.xs) textAlign = TextAlign.Justify,
) { color = MaterialTheme.colorScheme.onSurfaceVariant
Text( )
text = title,
fontWeight = FontWeight.Bold,
fontSize = 15.sp,
color = MaterialTheme.colorScheme.primary
)
Divider(color = MaterialTheme.colorScheme.outline.copy(alpha = 0.2f))
Text(
text = content,
fontSize = 13.sp,
lineHeight = 20.sp,
textAlign = TextAlign.Justify,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.85f)
)
}
} }
} }
/**
* کارت پایه‌ی صفحه‌ی جزئیات — ظاهر یکدست با بقیه‌ی برنامه:
* سطح روشن + قاب نازک هم‌رنگ تم، بدون سایه‌ی سنگین.
*/
@Composable
private fun DetailCard(
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit
) {
val shape = MaterialTheme.shapes.large
Column(
modifier = modifier
.fillMaxWidth()
.clip(shape)
.background(MaterialTheme.colorScheme.surfaceContainerLowest)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.outlineVariant,
shape = shape
)
.padding(MaterialTheme.dime.md),
content = content
)
}
/** سربرگ بخش با نوار تأکید کوچک کنار عنوان. */
@Composable
private fun SectionHeader(
title: String,
modifier: Modifier = Modifier
) {
val dime = LocalDime.current
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dime.sm)
) {
Box(
modifier = Modifier
.size(width = 4.dp, height = 18.dp)
.clip(RoundedCornerShape(dime.xxs))
.background(MaterialTheme.colorScheme.primary)
)
Text(
text = title,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
}
}
/** حالت «اطلاعاتی ثبت نشده» با آیکن، یکدست در همه‌ی تب‌ها. */
@Composable
private fun DetailEmptyState(
text: String,
modifier: Modifier = Modifier
) {
val dime = LocalDime.current
Column(
modifier = modifier
.fillMaxWidth()
.padding(vertical = dime.xxl, horizontal = dime.lg),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dime.sm)
) {
Icon(
imageVector = Icons.Default.Info,
contentDescription = null,
modifier = Modifier.size(40.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f)
)
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center
)
}
}
/** ردیف «برچسب: مقدار» با تایپوگرافی یکدست. */
@Composable
private fun LabeledRow(
label: String,
value: String,
modifier: Modifier = Modifier
) {
Row(modifier = modifier) {
Text(
text = "$label: ",
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.onSurface
)
Text(
text = value,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.weight(1f)
)
}
}
/** برچسب کوچک رنگی (مثل «پرخطر» / «حیاتی»). */
@Composable
private fun TagChip(
text: String,
container: Color,
onContainer: Color
) {
Text(
text = text,
style = MaterialTheme.typography.labelSmall,
color = onContainer,
modifier = Modifier
.clip(RoundedCornerShape(MaterialTheme.dime.xs))
.background(container)
.padding(horizontal = MaterialTheme.dime.sm, vertical = MaterialTheme.dime.xxs)
)
}
@@ -1,3 +1,3 @@
package com.approagency.pharmacy.ui.theme package com.approagency.pharmacy.ui.theme
// رنگ‌های برنامه در MaterialThemeExt.kt (MedicalColors و ColorScheme ها) تعریف شده‌اند. // رنگ‌های برنامه به‌صورت ColorScheme کامل (Light/Dark) در Theme.kt تعریف شده‌اند.
@@ -14,72 +14,96 @@ import androidx.compose.ui.platform.LocalContext
import com.fabirt.podcastapp.ui.theme.Shapes import com.fabirt.podcastapp.ui.theme.Shapes
import com.vada.caller.ui.theme.Dime import com.vada.caller.ui.theme.Dime
import com.vada.caller.ui.theme.LocalDime import com.vada.caller.ui.theme.LocalDime
object MedicalColors { // پالت کامل و هماهنگ بر پایه‌ی سبز-آبی پزشکی (teal).
// همه‌ی نقش‌های surface/container/outline تعریف شده‌اند تا کانتینرها (کارت‌ها،
// تکست‌فیلدها، باتم‌شیت‌ها) رنگ یکدست و متناسب با برند داشته باشند و به مقادیر
// پیش‌فرض بنفش‌گون Material برنگردند.
private val LightColorScheme = lightColorScheme(
primary = Color(0xFF0F766E),
onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFFC7ECE6),
onPrimaryContainer = Color(0xFF00201D),
val Primary = Color(0xFF0F766E) secondary = Color(0xFF4C6360),
val PrimaryContainer = Color(0xFFD7F3EF) onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFFCEE8E3),
onSecondaryContainer = Color(0xFF09201D),
val Secondary = Color(0xFF5B8E8A) tertiary = Color(0xFF3D6373),
val SecondaryContainer = Color(0xFFE3F3F1) onTertiary = Color(0xFFFFFFFF),
tertiaryContainer = Color(0xFFC0E8FB),
onTertiaryContainer = Color(0xFF001F2A),
val Tertiary = Color(0xFF8FBFB8) background = Color(0xFFF5FBF9),
onBackground = Color(0xFF171D1C),
val Background = Color(0xFFF8FCFB) surface = Color(0xFFF5FBF9),
val Surface = Color(0xFFFFFFFF) onSurface = Color(0xFF171D1C),
surfaceVariant = Color(0xFFDAE5E1),
onSurfaceVariant = Color(0xFF3F4947),
val DarkBackground = Color(0xFF0F1720) surfaceContainerLowest = Color(0xFFFFFFFF),
val DarkSurface = Color(0xFF17232B) surfaceContainerLow = Color(0xFFEFF6F3),
surfaceContainer = Color(0xFFE9F1EE),
surfaceContainerHigh = Color(0xFFE3ECE9),
surfaceContainerHighest = Color(0xFFDEE7E4),
val OnDark = Color(0xFFF5FAF9) outline = Color(0xFF6F7977),
val OnLight = Color(0xFF102A2A) outlineVariant = Color(0xFFBEC9C5),
val Error = Color(0xFFD64545) error = Color(0xFFBA1A1A),
onError = Color(0xFFFFFFFF),
errorContainer = Color(0xFFFFDAD6),
onErrorContainer = Color(0xFF410002),
val Outline = Color(0xFFD0E2DF) inverseSurface = Color(0xFF2B3231),
} inverseOnSurface = Color(0xFFECF2EF),
private val DarkColorScheme = darkColorScheme( inversePrimary = Color(0xFF80D5CB),
primary = Color(0xFF5DD4C3), scrim = Color(0xFF000000),
onPrimary = Color(0xFF003733),
secondary = Color(0xFF8CC9C1),
onSecondary = Color(0xFF0D2B29),
tertiary = Color(0xFFA7DCD4),
onTertiary = Color(0xFF102A2A),
background = MedicalColors.DarkBackground,
onBackground = MedicalColors.OnDark,
surface = MedicalColors.DarkSurface,
onSurface = MedicalColors.OnDark,
primaryContainer = Color(0xFF124A45),
secondaryContainer = Color(0xFF1D3D3A),
error = Color(0xFFFF8A80)
) )
private val LightColorScheme = lightColorScheme( private val DarkColorScheme = darkColorScheme(
primary = MedicalColors.Primary, primary = Color(0xFF5DD4C3),
onPrimary = Color.White, onPrimary = Color(0xFF00382F),
primaryContainer = Color(0xFF005048),
onPrimaryContainer = Color(0xFF7FF8E6),
secondary = MedicalColors.Secondary, secondary = Color(0xFFB0CCC6),
onSecondary = Color.White, onSecondary = Color(0xFF1B3531),
secondaryContainer = Color(0xFF324B47),
onSecondaryContainer = Color(0xFFCCE8E2),
tertiary = MedicalColors.Tertiary, tertiary = Color(0xFFA4CCDE),
onTertiary = MedicalColors.OnLight, onTertiary = Color(0xFF053543),
tertiaryContainer = Color(0xFF234C5B),
onTertiaryContainer = Color(0xFFC0E8FB),
background = MedicalColors.Background, background = Color(0xFF0E1514),
onBackground = MedicalColors.OnLight, onBackground = Color(0xFFDDE4E1),
surface = MedicalColors.Surface, surface = Color(0xFF0E1514),
onSurface = MedicalColors.OnLight, onSurface = Color(0xFFDDE4E1),
surfaceVariant = Color(0xFF3F4947),
onSurfaceVariant = Color(0xFFBEC9C5),
primaryContainer = MedicalColors.PrimaryContainer, surfaceContainerLowest = Color(0xFF090F0E),
secondaryContainer = MedicalColors.SecondaryContainer, surfaceContainerLow = Color(0xFF171D1C),
surfaceContainer = Color(0xFF1B211F),
surfaceContainerHigh = Color(0xFF252B2A),
surfaceContainerHighest = Color(0xFF303635),
outline = MedicalColors.Outline, outline = Color(0xFF899390),
error = MedicalColors.Error outlineVariant = Color(0xFF3F4947),
error = Color(0xFFFFB4AB),
onError = Color(0xFF690005),
errorContainer = Color(0xFF93000A),
onErrorContainer = Color(0xFFFFDAD6),
inverseSurface = Color(0xFFDDE4E1),
inverseOnSurface = Color(0xFF2B3231),
inversePrimary = Color(0xFF0F766E),
scrim = Color(0xFF000000),
) )
@Composable @Composable