style(application): optimize ingredients and chemical analysis layout with compact padding and responsive badges
Some checks failed
Deploy Canina / deploy (push) Successful in 1m33s
E2E Playwright Tests / Run Full E2E & Security Suites (push) Failing after 6s

This commit is contained in:
parsa aghaei 2026-08-29 08:58:14 +03:30
parent 05e26cab9f
commit 32e40a820c
3 changed files with 689 additions and 684 deletions

View File

@ -649,77 +649,82 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
</section>
)}
{/* Ingredients Section */}
<section className="bg-white rounded-2xl sm:rounded-[2.5rem] border border-medical-gray-200 p-4 sm:p-6 md:p-8 shadow-md space-y-4 sm:space-y-6">
<div className="flex items-center gap-2.5 sm:gap-4 border-b border-medical-gray-100 pb-3 sm:pb-4">
<div className="w-9 h-9 sm:w-12 sm:h-12 bg-canina-blue text-white rounded-xl sm:rounded-2xl flex items-center justify-center shadow-lg shrink-0">
<FlaskConical className="w-4 h-4 sm:w-6 sm:h-6" />
</div>
<h3 className="text-lg sm:text-2xl font-black text-medical-gray-900 italic font-vazir">ترکیبات و آنالیز علمی</h3>
</div>
<div className="space-y-6 sm:space-y-8">
{/* Ingredients Cards */}
{product.main_ingredients && product.main_ingredients.length > 0 && (
<div>
<h4 className="text-xs font-black text-medical-gray-400 uppercase tracking-widest mb-3 font-vazir">مواد تشکیلدهنده برتر</h4>
<div className="flex flex-wrap gap-2.5 sm:gap-3">
{product.main_ingredients.map((ing, idx) => {
const termKey = termKeys.find(key => {
const termObj = scientificTerms[key] || SCIENTIFIC_TERMS[key];
const termName = termObj?.term || key;
const ingClean = ing.toLowerCase();
const keyClean = key.toLowerCase();
const termNameClean = termName.toLowerCase();
return ingClean.includes(keyClean) ||
keyClean.includes(ingClean) ||
ingClean.includes(termNameClean) ||
termNameClean.includes(ingClean);
});
return (
<div key={idx} className="bg-medical-gray-50 border border-medical-gray-100 px-3.5 py-2 sm:px-5 sm:py-3 rounded-xl sm:rounded-2xl flex items-center gap-2 sm:gap-3 group hover:border-canina-blue/30 transition-all">
<span className="text-xs sm:text-sm font-bold text-medical-gray-700 font-vazir">
{termKey ? (
<Tooltip termKey={termKey} onWikiNavigate={(id) => router.push(`/wiki?term=${id}`)}>
{ing}
</Tooltip>
) : ing}
</span>
<Sparkles className="w-3 h-3 text-canina-blue opacity-40 group-hover:opacity-100 transition-opacity" />
</div>
);
})}
</div>
{/* Ingredients & Scientific Analysis Section (Compact & Sleek Design) */}
{((product.main_ingredients && product.main_ingredients.length > 0) || (product.analysis && Object.keys(product.analysis).length > 0)) && (
<section className="bg-white rounded-2xl sm:rounded-3xl border border-medical-gray-200 p-4 sm:p-6 shadow-sm space-y-4">
<div className="flex items-center gap-2.5 sm:gap-3 border-b border-medical-gray-100 pb-3">
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-canina-blue text-white rounded-xl flex items-center justify-center shadow-md shrink-0">
<FlaskConical className="w-4 h-4 sm:w-5 sm:h-5" />
</div>
)}
<div>
<h3 className="text-base sm:text-lg font-black text-medical-gray-900 italic font-vazir">ترکیبات و آنالیز علمی</h3>
<p className="text-[10px] sm:text-xs text-medical-gray-400 font-bold font-vazir mt-0.5">آنالیز دقیق مواد مؤثره و فرمولاسیون دارویی</p>
</div>
</div>
{/* Quantitative Analysis Cards Grid (Strict chemical percentages, not duplicate ingredients) */}
{(() => {
const filteredAnalysis = Object.entries(product.analysis || {}).filter(([key, val]) => {
const cleanKey = key.toLowerCase();
// Don't show duplicate ingredients here if already listed above
if (cleanKey.includes('مواد') || cleanKey.includes('ترکیبات') || cleanKey.includes('ingredient')) return false;
return Boolean(val);
});
if (filteredAnalysis.length === 0) return null;
return (
<div className="space-y-4 pt-4 border-t border-medical-gray-100">
<div className="text-xs font-black text-medical-gray-500 uppercase tracking-widest font-vazir">جدول آنالیز و آنالیتیکال ترکیبات</div>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
{filteredAnalysis.map(([key, value]) => (
<div key={key} className="bg-medical-gray-50 border border-medical-gray-200 p-5 rounded-2xl shadow-xs group hover:border-canina-blue/30 transition-all">
<div className="text-[11px] font-bold text-medical-gray-400 mb-1 font-vazir truncate">{key}</div>
<div className="text-xl font-black font-vazir tracking-tight text-canina-blue">{toPersian(value)}</div>
</div>
))}
<div className="space-y-4">
{/* Ingredients Pills */}
{product.main_ingredients && product.main_ingredients.length > 0 && (
<div className="space-y-2">
<h4 className="text-[11px] font-black text-medical-gray-400 uppercase tracking-wider font-vazir">مواد تشکیلدهنده برتر</h4>
<div className="flex flex-wrap gap-2">
{product.main_ingredients.map((ing, idx) => {
const termKey = termKeys.find(key => {
const termObj = scientificTerms[key] || SCIENTIFIC_TERMS[key];
const termName = termObj?.term || key;
const ingClean = ing.toLowerCase();
const keyClean = key.toLowerCase();
const termNameClean = termName.toLowerCase();
return ingClean.includes(keyClean) ||
keyClean.includes(ingClean) ||
ingClean.includes(termNameClean) ||
termNameClean.includes(ingClean);
});
return (
<div key={idx} className="bg-medical-gray-50 border border-medical-gray-200 px-3 py-1.5 rounded-xl flex items-center gap-1.5 group hover:border-canina-blue/30 hover:bg-canina-blue/5 transition-all">
<span className="text-xs sm:text-sm font-bold text-medical-gray-700 font-vazir">
{termKey ? (
<Tooltip termKey={termKey} onWikiNavigate={(id) => router.push(`/wiki?term=${id}`)}>
{ing}
</Tooltip>
) : ing}
</span>
<Sparkles className="w-3 h-3 text-canina-blue opacity-40 group-hover:opacity-100 transition-opacity" />
</div>
);
})}
</div>
</div>
);
})()}
</div>
</section>
)}
{/* Quantitative Analysis Cards (Responsive layout fitting 1 or multiple items naturally) */}
{(() => {
const filteredAnalysis = Object.entries(product.analysis || {}).filter(([key, val]) => {
const cleanKey = key.toLowerCase();
// Don't show duplicate ingredients here if already listed above
if (cleanKey.includes('مواد') || cleanKey.includes('ترکیبات') || cleanKey.includes('ingredient')) return false;
return Boolean(val);
});
if (filteredAnalysis.length === 0) return null;
return (
<div className="space-y-2.5 pt-3 border-t border-medical-gray-100">
<div className="text-[11px] font-black text-medical-gray-400 uppercase tracking-wider font-vazir">جدول آنالیز و آنالیتیکال ترکیبات</div>
<div className="flex flex-wrap gap-2.5">
{filteredAnalysis.map(([key, value]) => (
<div key={key} className="bg-medical-gray-50/80 border border-medical-gray-200 px-4 py-2.5 rounded-xl flex items-center justify-between gap-4 min-w-[180px] flex-1 max-w-sm hover:border-canina-blue/30 transition-all">
<span className="text-xs font-bold text-medical-gray-500 font-vazir truncate">{key}</span>
<span className="text-sm font-black font-vazir text-canina-blue whitespace-nowrap">{toPersian(value)}</span>
</div>
))}
</div>
</div>
);
})()}
</div>
</section>
)}
{/* Feeding / Usage Instructions Section (Merged and enriched) */}
<section className="bg-white border border-medical-gray-200 rounded-[2.5rem] p-8 md:p-10 shadow-md">

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff