Compare commits

..

2 Commits

Author SHA1 Message Date
parsa aghaei
8d1b786a24 chore(brand): rename all Persian occurrences of کانینا to کنینا across the project
All checks were successful
Deploy Canina / deploy (push) Successful in 1m50s
2026-08-16 16:40:08 +03:30
parsa aghaei
cbd7e1a24d feat(auth): implement WebOTP SMS auto-fill, auto-submit, password setup in dashboard, and password visibility toggle 2026-08-16 16:37:59 +03:30
80 changed files with 867 additions and 486 deletions

View File

@ -1,6 +1,6 @@
# نقشه جامع اتصال به بک‌بند (Comprehensive Backend Integration Map) 🏗️ # نقشه جامع اتصال به بک‌بند (Comprehensive Backend Integration Map) 🏗️
این سند حاوی معماری دقیق، فهرست کامل نقاط اتصال (API Endpoints)، کامنت‌های مکان‌نما در کد (TODO Markers)، و تعاریف رسمی تمامی تایپ‌های ورودی (Inputs) و خروجی (Outputs) پلتفرم درمانی و فروشگاهی کانینا ایران است. این سند حاوی معماری دقیق، فهرست کامل نقاط اتصال (API Endpoints)، کامنت‌های مکان‌نما در کد (TODO Markers)، و تعاریف رسمی تمامی تایپ‌های ورودی (Inputs) و خروجی (Outputs) پلتفرم درمانی و فروشگاهی کنینا ایران است.
--- ---

View File

@ -1,6 +1,6 @@
# طرح‌واره پایگاه داده کانینا ایران (Canina Iran Database Schema) 🗄️ # طرح‌واره پایگاه داده کنینا ایران (Canina Iran Database Schema) 🗄️
این سند ساختار دقیق جداول، فیلدها، نوع داده‌ها (Datatypes)، روابط بین موجودیت‌ها (ERD Relations) و ایندکس‌های پیشنهادی را برای راه‌اندازی پایگاه داده نهایی سیستم کانینا طراحی و ارائه می‌کند. جهت انعطاف‌پذیری توسعه، پیاده‌سازی هم بر اساس **پایگاه داده رابطه‌ای (PostgreSQL / MySQL)** و هم **پایگاه داده مستندمحور/سندمراقبتی (NoSQL - MongoDB / Firestore)** مدلسازی شده است. این سند ساختار دقیق جداول، فیلدها، نوع داده‌ها (Datatypes)، روابط بین موجودیت‌ها (ERD Relations) و ایندکس‌های پیشنهادی را برای راه‌اندازی پایگاه داده نهایی سیستم کنینا طراحی و ارائه می‌کند. جهت انعطاف‌پذیری توسعه، پیاده‌سازی هم بر اساس **پایگاه داده رابطه‌ای (PostgreSQL / MySQL)** و هم **پایگاه داده مستندمحور/سندمراقبتی (NoSQL - MongoDB / Firestore)** مدلسازی شده است.
--- ---
@ -63,7 +63,7 @@ CREATE TABLE wallet_transactions (
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
); );
-- ۴. جدول کاتالوگ محصولات کانینا (products) -- ۴. جدول کاتالوگ محصولات کنینا (products)
CREATE TABLE products ( CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
art_no VARCHAR(20) UNIQUE NOT NULL, art_no VARCHAR(20) UNIQUE NOT NULL,
@ -250,7 +250,7 @@ CREATE INDEX idx_reminder_completions_ref ON reminder_completions(reminder_id, c
"reminders": [ "reminders": [
{ {
"id": "REM-001", "id": "REM-001",
"productId": "6472f8d38bfcb8234eccb611", // مکمل آرتروفیت کانینا فیزیکی جهت کسر انبار پت "productId": "6472f8d38bfcb8234eccb611", // مکمل آرتروفیت کنینا فیزیکی جهت کسر انبار پت
"title": "مصرف قرص مفصل‌ساز Canhydrox", "title": "مصرف قرص مفصل‌ساز Canhydrox",
"time": "08:30", "time": "08:30",
"frequency": "روزانه", "frequency": "روزانه",

View File

@ -1,7 +1,7 @@
# پلتفرم کانینا ایران (Canina Iran Platform) 🐾 # پلتفرم کنینا ایران (Canina Iran Platform) 🐾
### لندینگ، فروشگاه تخصصی و پرونده سلامت الکترونیک پت (نمایندگی رسمی دارویی Canina Pharma) ### لندینگ، فروشگاه تخصصی و پرونده سلامت الکترونیک پت (نمایندگی رسمی دارویی Canina Pharma)
پلتفرم **کانینا ایران** یک برنامه جامع تحت وب (SPA/PWA) مجهز به بردهای تعاملی و ابزارهای بهداشتی-دامپزشکی است که با هدف ارتقای سلامت حیوانات خانگی و دسترسی آسان‌تر به مکمل‌های دارویی گرید فوق‌ارگانیک و دارویی کمپانی والای **Canina pharma GmbH آلمان** توسعه یافته است. پلتفرم **کنینا ایران** یک برنامه جامع تحت وب (SPA/PWA) مجهز به بردهای تعاملی و ابزارهای بهداشتی-دامپزشکی است که با هدف ارتقای سلامت حیوانات خانگی و دسترسی آسان‌تر به مکمل‌های دارویی گرید فوق‌ارگانیک و دارویی کمپانی والای **Canina pharma GmbH آلمان** توسعه یافته است.
--- ---

View File

@ -24,7 +24,7 @@ async function main() {
'<p>کلسیم یکی از مهم‌ترین مواد معدنی برای رشد ساختار استخوانی است. توله‌سگ‌ها در ماه‌های اول زندگی به کلسیم بیشتری نیاز دارند تا اسکلت‌بندی محکمی داشته باشند.</p>', '<p>کلسیم یکی از مهم‌ترین مواد معدنی برای رشد ساختار استخوانی است. توله‌سگ‌ها در ماه‌های اول زندگی به کلسیم بیشتری نیاز دارند تا اسکلت‌بندی محکمی داشته باشند.</p>',
'', '',
'<h2>نسبت Ca:P (کلسیم به فسفر)</h2>', '<h2>نسبت Ca:P (کلسیم به فسفر)</h2>',
'<p>نسبت ایده‌آل کلسیم به فسفر در رژیم توله‌سگ‌ها <strong>۱.۲:۱ تا ۱.۵:۱</strong> است. عدم تعادل در این نسبت می‌تواند منجر به ناهنجاری‌های استخوانی مانند پاهای پرانتزی و دیسپلازی هیپ شود. <strong>پودر پوسته تخم مرغ کانینا</strong> با ۳۶٪ کلسیم خالص و فسفر ۰.۱٪ بهترین گزینه برای تنظیم این نسبت است.</p>', '<p>نسبت ایده‌آل کلسیم به فسفر در رژیم توله‌سگ‌ها <strong>۱.۲:۱ تا ۱.۵:۱</strong> است. عدم تعادل در این نسبت می‌تواند منجر به ناهنجاری‌های استخوانی مانند پاهای پرانتزی و دیسپلازی هیپ شود. <strong>پودر پوسته تخم مرغ کنینا</strong> با ۳۶٪ کلسیم خالص و فسفر ۰.۱٪ بهترین گزینه برای تنظیم این نسبت است.</p>',
'', '',
'<h2>نقش ویتامین D3 در جذب کلسیم</h2>', '<h2>نقش ویتامین D3 در جذب کلسیم</h2>',
'<p>ویتامین D3 (کوله‌کلسیفرول) جذب کلسیم از روده را تا ۳۰٪ افزایش می‌دهد. <strong>مصرف همزمان ویتامین D2 (ارگوکلسیفرول) با D3 مجاز نیست</strong> زیرا این دو با یکدیگر رقابت کرده و جذب مؤثر را کاهش می‌دهند.</p>', '<p>ویتامین D3 (کوله‌کلسیفرول) جذب کلسیم از روده را تا ۳۰٪ افزایش می‌دهد. <strong>مصرف همزمان ویتامین D2 (ارگوکلسیفرول) با D3 مجاز نیست</strong> زیرا این دو با یکدیگر رقابت کرده و جذب مؤثر را کاهش می‌دهند.</p>',
@ -49,12 +49,12 @@ async function main() {
'<li><strong>B1 (تیامین):</strong> متابولیسم کربوهیدرات‌ها و عملکرد سیستم عصبی</li>', '<li><strong>B1 (تیامین):</strong> متابولیسم کربوهیدرات‌ها و عملکرد سیستم عصبی</li>',
'<li><strong>B2 (ریبوفلاوین):</strong> تولید انرژی و سلامت پوست</li>', '<li><strong>B2 (ریبوفلاوین):</strong> تولید انرژی و سلامت پوست</li>',
'<li><strong>B6 (پیریدوکسین):</strong> سنتز پروتئین و تولید گلبول قرمز</li>', '<li><strong>B6 (پیریدوکسین):</strong> سنتز پروتئین و تولید گلبول قرمز</li>',
'<li><strong>B7 (بیوتین / ویتامین H):</strong> کراتین‌سازی مو و پنجه - <strong>Biotin-Tabs کانینا</strong> با ۱ میلیون میکروگرم بیوتین در کیلوگرم، قوی‌ترین دوز موجود در بازار است</li>', '<li><strong>B7 (بیوتین / ویتامین H):</strong> کراتین‌سازی مو و پنجه - <strong>Biotin-Tabs کنینا</strong> با ۱ میلیون میکروگرم بیوتین در کیلوگرم، قوی‌ترین دوز موجود در بازار است</li>',
'<li><strong>B12 (کوبالامین):</strong> خونسازی و عملکرد عصبی</li>', '<li><strong>B12 (کوبالامین):</strong> خونسازی و عملکرد عصبی</li>',
'</ul>', '</ul>',
'', '',
'<h2>نقش بیوتین در سلامت پوشش</h2>', '<h2>نقش بیوتین در سلامت پوشش</h2>',
'<p>بیوتین کوآنزیم کربوکسیلازهای مسئول سنتز اسیدهای چرب و گلوکونئوژنز است. <strong>ریزش مو و شکنندگی پنجه</strong> از نشانه‌های کلاسیک کمبود بیوتین هستند. قرص بیوتین کانینا با تأثیر از هفته چهارم، پوشش براق و پنجه‌های محکم را تضمین می‌کند.</p>', '<p>بیوتین کوآنزیم کربوکسیلازهای مسئول سنتز اسیدهای چرب و گلوکونئوژنز است. <strong>ریزش مو و شکنندگی پنجه</strong> از نشانه‌های کلاسیک کمبود بیوتین هستند. قرص بیوتین کنینا با تأثیر از هفته چهارم، پوشش براق و پنجه‌های محکم را تضمین می‌کند.</p>',
'', '',
'<h2>محصولات تقویت‌کننده پوست و مو</h2>', '<h2>محصولات تقویت‌کننده پوست و مو</h2>',
'<ul>', '<ul>',

View File

@ -39,7 +39,7 @@ export async function main() {
vetName: 'دکتر علیرضا رضایی', vetName: 'دکتر علیرضا رضایی',
clinicName: 'کلینیک دامپزشکی پایتخت', clinicName: 'کلینیک دامپزشکی پایتخت',
imageUrl: '/assets/images/regenerated_image_1779109861747.png', imageUrl: '/assets/images/regenerated_image_1779109861747.png',
quote: 'مکمل‌های مفصلی کانینا از نظر سرعت جذب و اثربخشی در سگ‌های نژاد بزرگ بی‌نظیر هستند. ما نتایج فوق‌العاده‌ای روی دیسپلازی هیپ مشاهده کردیم.', quote: 'مکمل‌های مفصلی کنینا از نظر سرعت جذب و اثربخشی در سگ‌های نژاد بزرگ بی‌نظیر هستند. ما نتایج فوق‌العاده‌ای روی دیسپلازی هیپ مشاهده کردیم.',
rating: 5, rating: 5,
order: 1, order: 1,
isActive: true, isActive: true,
@ -48,7 +48,7 @@ export async function main() {
vetName: 'دکتر سارا شمس', vetName: 'دکتر سارا شمس',
clinicName: 'بیمارستان دامپزشکی تهران', clinicName: 'بیمارستان دامپزشکی تهران',
imageUrl: '/assets/images/regenerated_image_1779109861747.png', imageUrl: '/assets/images/regenerated_image_1779109861747.png',
quote: 'من همیشه برای ریزش مو و مشکلات پوستی گربه‌ها، قرص‌های مخمر و بیوتین کانینا را تجویز می‌کنم. نتایج معمولاً در کمتر از ۳ هفته قابل مشاهده است.', quote: 'من همیشه برای ریزش مو و مشکلات پوستی گربه‌ها، قرص‌های مخمر و بیوتین کنینا را تجویز می‌کنم. نتایج معمولاً در کمتر از ۳ هفته قابل مشاهده است.',
rating: 5, rating: 5,
order: 2, order: 2,
isActive: true, isActive: true,

View File

@ -1,8 +1,8 @@
[ [
{ {
"name": "Canina Ballaststoff Mix (فیبر میکس کانینا)", "name": "Canina Ballaststoff Mix (فیبر میکس کنینا)",
"category_slug": "immune", "category_slug": "immune",
"description": "فیبر میکس کانینا حاوی منابع فیبر گیاهی از سیب، یونجه، نخود فرنگی و هویج است که به طور روزانه رژیم غذایی خام پت را تقویت می‌کند. فیبرها برای فلور پایدار روده مهم هستند؛ به عنوان پری‌بیوتیک، آن‌ها به عنوان غذا برای باکتری‌های روده بزرگ عمل می‌کنند. اجزای گیاهی، همی‌سلولز، سلولز، لیگنین و مواد معدنی طبیعی مانند منیزیم و پتاسیم را فراهم می‌کنند. فیبر میکس کانینا را می‌توان به هر نوع غذایی اضافه کرد. حتی رژیم‌های غذایی خام و غذاهای خانگی اغلب فاقد فیبری هستند که حرکت طبیعی روده را تحریک کرده و مدفوع را حجیم‌تر می‌کند. این فیبرها می‌توانند تأثیر مثبتی بر حرکات دودی روده، مدفوع و تخلیه غدد مقعدی داشته باشند. مانان الیگوساکارید و بتا گلوکان از دیواره سلولی مخمر سیستم ایمنی روده را تقویت می‌کنند. بتا گلوکان‌ها به عنوان منبع انرژی برای باکتری‌های روده بزرگ عمل می‌کنند و توسط آن‌ها به اسیدهای چرب زنجیره کوتاه تبدیل می‌شوند که به نوبه خود به عنوان ماده مغذی برای سلول‌های دیواره روده عمل می‌کنند.", "description": "فیبر میکس کنینا حاوی منابع فیبر گیاهی از سیب، یونجه، نخود فرنگی و هویج است که به طور روزانه رژیم غذایی خام پت را تقویت می‌کند. فیبرها برای فلور پایدار روده مهم هستند؛ به عنوان پری‌بیوتیک، آن‌ها به عنوان غذا برای باکتری‌های روده بزرگ عمل می‌کنند. اجزای گیاهی، همی‌سلولز، سلولز، لیگنین و مواد معدنی طبیعی مانند منیزیم و پتاسیم را فراهم می‌کنند. فیبر میکس کنینا را می‌توان به هر نوع غذایی اضافه کرد. حتی رژیم‌های غذایی خام و غذاهای خانگی اغلب فاقد فیبری هستند که حرکت طبیعی روده را تحریک کرده و مدفوع را حجیم‌تر می‌کند. این فیبرها می‌توانند تأثیر مثبتی بر حرکات دودی روده، مدفوع و تخلیه غدد مقعدی داشته باشند. مانان الیگوساکارید و بتا گلوکان از دیواره سلولی مخمر سیستم ایمنی روده را تقویت می‌کنند. بتا گلوکان‌ها به عنوان منبع انرژی برای باکتری‌های روده بزرگ عمل می‌کنند و توسط آن‌ها به اسیدهای چرب زنجیره کوتاه تبدیل می‌شوند که به نوبه خود به عنوان ماده مغذی برای سلول‌های دیواره روده عمل می‌کنند.",
"short_description": "فیبر گیاهی پری‌بیوتیک برای تنظیم فلور روده و بهبود گوارش سگ و گربه.", "short_description": "فیبر گیاهی پری‌بیوتیک برای تنظیم فلور روده و بهبود گوارش سگ و گربه.",
"ingredients": "تفاله سیب، پودر یونجه سبز، فیبر نخود فرنگی، تفاله هویج، کیک بذر کتان، کمپلکس الیگوساکارید و بتا-گلوکان، مخمر آبجو، کنگر فرنگی، خاک دیاتومه، روغن کلزا، کربنات کلسیم.", "ingredients": "تفاله سیب، پودر یونجه سبز، فیبر نخود فرنگی، تفاله هویج، کیک بذر کتان، کمپلکس الیگوساکارید و بتا-گلوکان، مخمر آبجو، کنگر فرنگی، خاک دیاتومه، روغن کلزا، کربنات کلسیم.",
"usage_and_dosage": "گرم به ازای هر کیلوگرم وزن بدن، حداکثر 15 گرم در روز.", "usage_and_dosage": "گرم به ازای هر کیلوگرم وزن بدن، حداکثر 15 گرم در روز.",
@ -34,9 +34,9 @@
] ]
}, },
{ {
"name": "Canina Eierschalenpulver (پودر پوسته تخم مرغ کانینا)", "name": "Canina Eierschalenpulver (پودر پوسته تخم مرغ کنینا)",
"category_slug": "energy", "category_slug": "energy",
"description": "پودر پوسته تخم مرغ کانینا نه تنها کلسیم معدنی مهم مورد نیاز برای متابولیسم استخوان، سلامت دندان‌ها و فعالیت عضلات را تأمین می‌کند، بلکه سایر مواد معدنی طبیعی مانند منیزیم، پتاسیم، سدیم، آهن، مس و روی را نیز تأمین می‌کند. از آنجایی که پودر پوسته تخم مرغ کانینا ریز آسیاب شده است، به خوبی تحمل می‌شود و بنابراین جذب کلسیم بسیار بالا است. پودر پوسته تخم مرغ کانینا یک جایگزین با کیفیت بالا برای پودر استخوان گوشت برای سگ‌هایی است که به پروتئین‌های گوشتی مانند گوشت گاو، مرغ یا خوک حساسیت دارند. پودر پوسته تخم مرغ کانینا به دلیل فسفر کم و کلسیم بالا، به ویژه برای تکمیل وعده‌های غذایی گوشتی خام غنی از فسفر و کم کلسیم یا آماده‌سازی‌های گوشتی پخته شده مناسب است.", "description": "پودر پوسته تخم مرغ کنینا نه تنها کلسیم معدنی مهم مورد نیاز برای متابولیسم استخوان، سلامت دندان‌ها و فعالیت عضلات را تأمین می‌کند، بلکه سایر مواد معدنی طبیعی مانند منیزیم، پتاسیم، سدیم، آهن، مس و روی را نیز تأمین می‌کند. از آنجایی که پودر پوسته تخم مرغ کنینا ریز آسیاب شده است، به خوبی تحمل می‌شود و بنابراین جذب کلسیم بسیار بالا است. پودر پوسته تخم مرغ کنینا یک جایگزین با کیفیت بالا برای پودر استخوان گوشت برای سگ‌هایی است که به پروتئین‌های گوشتی مانند گوشت گاو، مرغ یا خوک حساسیت دارند. پودر پوسته تخم مرغ کنینا به دلیل فسفر کم و کلسیم بالا، به ویژه برای تکمیل وعده‌های غذایی گوشتی خام غنی از فسفر و کم کلسیم یا آماده‌سازی‌های گوشتی پخته شده مناسب است.",
"short_description": "تامین‌کننده کلسیم ارگانیک و طبیعی مناسب رژیم خام گوشتی.", "short_description": "تامین‌کننده کلسیم ارگانیک و طبیعی مناسب رژیم خام گوشتی.",
"ingredients": "100% پوسته تخم مرغ آسیاب شده. خاکستر خام 96%، کلسیم 36%، فسفر 0.1%.", "ingredients": "100% پوسته تخم مرغ آسیاب شده. خاکستر خام 96%، کلسیم 36%، فسفر 0.1%.",
"usage_and_dosage": "0.2 گرم به ازای هر کیلوگرم وزن بدن در روز.", "usage_and_dosage": "0.2 گرم به ازای هر کیلوگرم وزن بدن در روز.",
@ -51,9 +51,9 @@
] ]
}, },
{ {
"name": "Canina FLEXAN (فلکسان کانینا)", "name": "Canina FLEXAN (فلکسان کنینا)",
"category_slug": "joints", "category_slug": "joints",
"description": "فلکسان کانینا به طور طبیعی از سیستم اسکلتی عضلانی سگ شما پشتیبانی می‌کند. مفاصل متحرک، استخوان‌های پایدار، تحرک و تناسب اندام - پپتیدهای کلاژن زیست‌فعال، بلوک‌های سازنده اولیه بدن هستند. کلاژن پروتئین ساختاری اصلی بافت همبند است. نشان داده شده است که تأمین هدفمند هیدرولیز کلاژن، سلول‌های غضروف را فعال می‌کند و بنابراین به طور بهینه از بازسازی غضروف مفاصل پشتیبانی کرده و روند تخریب آن را کند می‌کند. همچنین سلول‌های تشکیل‌دهنده استخوان را تحریک کرده و بافت استخوان را از نظر پایداري، انعطاف‌پذیري، ماده و چگالی بهبود می‌بخشد. کلاژن زیست‌فعال موجود در مواد اولیه طبیعی با منشأ حیوانی تولید می‌شود. پپتیدهای کلاژن با فراهمی زیستی عالی مشخص می‌شوند و تقریباً بدون از دست رفتن توسط بدن جذب می‌شوند. پودر گوشت صدف لب سبز از صدف لب سبز نیوزیلند بلوک‌های ساختمانی ارزشمندی (گلیکوزآمینوگلیکان‌ها) را برای غضروف مفصل فراهم می‌کند و تمام بافت همبند را تقویت می‌کند. پپتیدهای کلاژن زیست‌فعال و پودر گوشت صدف لب سبز به صورت هم‌افزایی با هم کار می‌کنند و تأثیر را بر کل سیستم اسکلتی عضلانی بهینه می‌کنند.", "description": "فلکسان کنینا به طور طبیعی از سیستم اسکلتی عضلانی سگ شما پشتیبانی می‌کند. مفاصل متحرک، استخوان‌های پایدار، تحرک و تناسب اندام - پپتیدهای کلاژن زیست‌فعال، بلوک‌های سازنده اولیه بدن هستند. کلاژن پروتئین ساختاری اصلی بافت همبند است. نشان داده شده است که تأمین هدفمند هیدرولیز کلاژن، سلول‌های غضروف را فعال می‌کند و بنابراین به طور بهینه از بازسازی غضروف مفاصل پشتیبانی کرده و روند تخریب آن را کند می‌کند. همچنین سلول‌های تشکیل‌دهنده استخوان را تحریک کرده و بافت استخوان را از نظر پایداري، انعطاف‌پذیري، ماده و چگالی بهبود می‌بخشد. کلاژن زیست‌فعال موجود در مواد اولیه طبیعی با منشأ حیوانی تولید می‌شود. پپتیدهای کلاژن با فراهمی زیستی عالی مشخص می‌شوند و تقریباً بدون از دست رفتن توسط بدن جذب می‌شوند. پودر گوشت صدف لب سبز از صدف لب سبز نیوزیلند بلوک‌های ساختمانی ارزشمندی (گلیکوزآمینوگلیکان‌ها) را برای غضروف مفصل فراهم می‌کند و تمام بافت همبند را تقویت می‌کند. پپتیدهای کلاژن زیست‌فعال و پودر گوشت صدف لب سبز به صورت هم‌افزایی با هم کار می‌کنند و تأثیر را بر کل سیستم اسکلتی عضلانی بهینه می‌کنند.",
"short_description": "پپتیدهای کلاژن زیست‌فعال برای بازسازی غضروف و بهبود تحرک سگ.", "short_description": "پپتیدهای کلاژن زیست‌فعال برای بازسازی غضروف و بهبود تحرک سگ.",
"ingredients": "90% کلاژن هیدرولیز شده، 10% پودر گوشت صدف لب سبز.", "ingredients": "90% کلاژن هیدرولیز شده، 10% پودر گوشت صدف لب سبز.",
"usage_and_dosage": "2 گرم به ازای هر 10 کیلوگرم وزن بدن، حداکثر 15 گرم در روز.", "usage_and_dosage": "2 گرم به ازای هر 10 کیلوگرم وزن بدن، حداکثر 15 گرم در روز.",
@ -68,7 +68,7 @@
] ]
}, },
{ {
"name": "Canina HERZ VITAL (هرز ویتال کانینا)", "name": "Canina HERZ VITAL (هرز ویتال کنینا)",
"category_slug": "general", "category_slug": "general",
"description": "سگ‌ها و گربه‌های مسن اغلب از نارسایی قلبی رنج می‌برند. ال-کارنیتین می‌تواند بر تأمین انرژی قلب و عضلات اسکلتی تأثیر مثبت بگذارد. این ماده می‌تواند نشاط، انگیزه و رفاه سگ/گربه شما را افزایش دهد. ال-کارنیتین می‌تواند عملکرد فیزیولوژیکی قلب را تقویت کند.", "description": "سگ‌ها و گربه‌های مسن اغلب از نارسایی قلبی رنج می‌برند. ال-کارنیتین می‌تواند بر تأمین انرژی قلب و عضلات اسکلتی تأثیر مثبت بگذارد. این ماده می‌تواند نشاط، انگیزه و رفاه سگ/گربه شما را افزایش دهد. ال-کارنیتین می‌تواند عملکرد فیزیولوژیکی قلب را تقویت کند.",
"short_description": "تقویت‌کننده طبیعی کارکرد ماهیچه قلب برای سگ و گربه مسن.", "short_description": "تقویت‌کننده طبیعی کارکرد ماهیچه قلب برای سگ و گربه مسن.",
@ -85,9 +85,9 @@
] ]
}, },
{ {
"name": "Canina IMMUN-BOOSTER (ایمون بوستر کانینا)", "name": "Canina IMMUN-BOOSTER (ایمون بوستر کنینا)",
"category_slug": "immune", "category_slug": "immune",
"description": "بازیابی و نقاهت تغذیه‌ای > چگالی انرژی بالا، غلظت بالای مواد مغذی ضروری و مواد تشکیل‌دهنده با قابلیت هضم بالا. تقویت‌کننده سیستم ایمنی کانینا انرژی قابل هضم و در دسترس، ویتامین‌های با غلظت بالا و ایمونوگلوبولین‌های آغوز را برای سگ‌ها و گربه‌ها فراهم می‌کند. این اولین شیری است که مادر در طول و بعد از تولد تولید می‌کند. این شیر سرشار از لاکتوفرین و لیزوزیم است. تقویت‌کننده سیستم ایمنی کانینا از سیستم دفاعی بدن پشتیبانی می‌کند و مرحله بهبودی حیوانات را، به ویژه در حین و بعد از فعالیت‌های بدنی مانند زایمان (حیوان مادر و همچنین توله سگ‌ها و بچه گربه‌ها)، تمرین، ورزش و آزمایش، بهینه می‌کند. میکروب تشکیل‌دهنده اسید لاکتیک، انتروکوک فاسیوم، در تقویت‌کننده سیستم ایمنی کانینا، کلونیزاسیون سریع با لاکتوباسیل‌های مهم را تضمین می‌کند. این باکتری‌ها برای فلور روده سالم و متعادل و در نتیجه برای سیستم ایمنی پایدار روده ضروری هستند. اتصال میکروب‌های منفی به مخاط روده خنثی می‌شود. این محصول یک خمیر خوشمزه است که سگ‌ها و گربه‌ها از آن لذت می‌برند. این محصول از توله سگ‌ها/بچه گربه‌ها تا گربه‌های مسن مناسب است. برای حمایت از سلامت و عملکرد، این محصول می‌تواند جیره غذایی روزانه را افزایش دهد. این خمیر همچنین می‌تواند در مراحل خاصی از زندگی که غذای روزانه به طور مطلوب نیازهای بالای حیوان را برآورده نمی‌کند، تجویز شود.", "description": "بازیابی و نقاهت تغذیه‌ای > چگالی انرژی بالا، غلظت بالای مواد مغذی ضروری و مواد تشکیل‌دهنده با قابلیت هضم بالا. تقویت‌کننده سیستم ایمنی کنینا انرژی قابل هضم و در دسترس، ویتامین‌های با غلظت بالا و ایمونوگلوبولین‌های آغوز را برای سگ‌ها و گربه‌ها فراهم می‌کند. این اولین شیری است که مادر در طول و بعد از تولد تولید می‌کند. این شیر سرشار از لاکتوفرین و لیزوزیم است. تقویت‌کننده سیستم ایمنی کنینا از سیستم دفاعی بدن پشتیبانی می‌کند و مرحله بهبودی حیوانات را، به ویژه در حین و بعد از فعالیت‌های بدنی مانند زایمان (حیوان مادر و همچنین توله سگ‌ها و بچه گربه‌ها)، تمرین، ورزش و آزمایش، بهینه می‌کند. میکروب تشکیل‌دهنده اسید لاکتیک، انتروکوک فاسیوم، در تقویت‌کننده سیستم ایمنی کنینا، کلونیزاسیون سریع با لاکتوباسیل‌های مهم را تضمین می‌کند. این باکتری‌ها برای فلور روده سالم و متعادل و در نتیجه برای سیستم ایمنی پایدار روده ضروری هستند. اتصال میکروب‌های منفی به مخاط روده خنثی می‌شود. این محصول یک خمیر خوشمزه است که سگ‌ها و گربه‌ها از آن لذت می‌برند. این محصول از توله سگ‌ها/بچه گربه‌ها تا گربه‌های مسن مناسب است. برای حمایت از سلامت و عملکرد، این محصول می‌تواند جیره غذایی روزانه را افزایش دهد. این خمیر همچنین می‌تواند در مراحل خاصی از زندگی که غذای روزانه به طور مطلوب نیازهای بالای حیوان را برآورده نمی‌کند، تجویز شود.",
"short_description": "خمیر انرژی‌بخش و ایمن‌ساز حاوی آغوز و پروبیوتیک فعال.", "short_description": "خمیر انرژی‌بخش و ایمن‌ساز حاوی آغوز و پروبیوتیک فعال.",
"ingredients": "روغن ها و چربی های گیاهی، پودر آغوز 20%، شکر انگور.", "ingredients": "روغن ها و چربی های گیاهی، پودر آغوز 20%، شکر انگور.",
"usage_and_dosage": "توله سگ ها: 0.25 میلی لیتر. سگ های متوسط: 0.5 تا 1 میلی لیتر. گربه ها: 0.25 میلی لیتر.", "usage_and_dosage": "توله سگ ها: 0.25 میلی لیتر. سگ های متوسط: 0.5 تا 1 میلی لیتر. گربه ها: 0.25 میلی لیتر.",
@ -102,7 +102,7 @@
] ]
}, },
{ {
"name": "Canina Katzenmilch (شیر گربه کانینا)", "name": "Canina Katzenmilch (شیر گربه کنینا)",
"category_slug": "nutrition", "category_slug": "nutrition",
"description": "غذای کامل جایگزین شیر مادر با اجزای سازنده بیولوژیکی ارزشمند: انرژی و قدرت. غذا با تمام ویتامین‌ها و مواد معدنی لازم برای رشد سالم. جایگزین شیر مادر برای بچه‌گربه‌ها از ۱ روزگی، برای تغذیه بچه‌گربه‌های بی‌مادر، اگر تولید شیر مادر کافی نیست، برای توله‌های بزرگ، برای آماده‌سازی و در طول شیردهی، به عنوان مکمل برای گربه‌های مولد، به عنوان مکمل برای گربه‌های مسن و بیمار. فرمول این شیر بر اساس شیر طبیعی گربه مادر تهیه شده است.", "description": "غذای کامل جایگزین شیر مادر با اجزای سازنده بیولوژیکی ارزشمند: انرژی و قدرت. غذا با تمام ویتامین‌ها و مواد معدنی لازم برای رشد سالم. جایگزین شیر مادر برای بچه‌گربه‌ها از ۱ روزگی، برای تغذیه بچه‌گربه‌های بی‌مادر، اگر تولید شیر مادر کافی نیست، برای توله‌های بزرگ، برای آماده‌سازی و در طول شیردهی، به عنوان مکمل برای گربه‌های مولد، به عنوان مکمل برای گربه‌های مسن و بیمار. فرمول این شیر بر اساس شیر طبیعی گربه مادر تهیه شده است.",
"short_description": "شیر خشک غنی از تورین جایگزین کامل شیر مادر برای بچه‌گربه‌ها.", "short_description": "شیر خشک غنی از تورین جایگزین کامل شیر مادر برای بچه‌گربه‌ها.",
@ -119,9 +119,9 @@
] ]
}, },
{ {
"name": "Canina Lachsol (روغن ماهی سالمون کانینا)", "name": "Canina Lachsol (روغن ماهی سالمون کنینا)",
"category_slug": "nutrition", "category_slug": "nutrition",
"description": "روغن ماهی سالمون کانینا از روغن ماهی سالمون ۱۰۰٪ خالص با کیفیت غذایی ساخته شده و سرشار از اسیدهای چرب چند غیراشباع (اسیدهای چرب امگا، EPA و DPA) است. از آنجایی که این اسیدهای چرب نمی‌توانند توسط خود سگ‌ها و گربه‌ها تولید شوند، باید از منابع خارجی در غذای آنها تأمین شوند. آنها برای متابولیسم روان ضروری و حیاتی هستند. روغن ماهی سالمون کانینا از بازسازی پوست و رشد مو پشتیبانی می‌کند و کل فرآیندهای متابولیکی بدن را هماهنگ می‌کند. صرف نظر از نوع خوراک، روغن ماهی سالمون کانینا غذای حیوان شما را به عنوان یک ماده خوراکی با کیفیت بالا بهینه می‌کند.", "description": "روغن ماهی سالمون کنینا از روغن ماهی سالمون ۱۰۰٪ خالص با کیفیت غذایی ساخته شده و سرشار از اسیدهای چرب چند غیراشباع (اسیدهای چرب امگا، EPA و DPA) است. از آنجایی که این اسیدهای چرب نمی‌توانند توسط خود سگ‌ها و گربه‌ها تولید شوند، باید از منابع خارجی در غذای آنها تأمین شوند. آنها برای متابولیسم روان ضروری و حیاتی هستند. روغن ماهی سالمون کنینا از بازسازی پوست و رشد مو پشتیبانی می‌کند و کل فرآیندهای متابولیکی بدن را هماهنگ می‌کند. صرف نظر از نوع خوراک، روغن ماهی سالمون کنینا غذای حیوان شما را به عنوان یک ماده خوراکی با کیفیت بالا بهینه می‌کند.",
"short_description": "روغن سالمون ۱۰۰٪ خالص برای براقیت پوست و مو و تنظیم متابولیسم.", "short_description": "روغن سالمون ۱۰۰٪ خالص برای براقیت پوست و مو و تنظیم متابولیسم.",
"ingredients": "100% روغن طبیعی ماهی سالمون.", "ingredients": "100% روغن طبیعی ماهی سالمون.",
"usage_and_dosage": "2 میلی لیتر به ازای هر 10 کیلوگرم وزن بدن روزانه.", "usage_and_dosage": "2 میلی لیتر به ازای هر 10 کیلوگرم وزن بدن روزانه.",
@ -136,7 +136,7 @@
] ]
}, },
{ {
"name": "Canina Marine-Olmischung PREMIUM (روغن دریایی پریمیوم کانینا)", "name": "Canina Marine-Olmischung PREMIUM (روغن دریایی پریمیوم کنینا)",
"category_slug": "nutrition", "category_slug": "nutrition",
"description": "روغن دریایی پریمیوم، ترکیبی هماهنگ و ویژه از روغن‌های خوراکی با کیفیت بالا است که در اثرات مثبت خود بر کل ارگانیسم، یکدیگر را به طور بهینه تکمیل می‌کنند. روغن دریایی امگا ۳ طبیعی (کنترل شده با آفت‌کش) سرشار از اسیدهای چرب اشباع نشده EPA و DHA است که برای سگ‌ها و گربه‌ها ضروری هستند. این اسیدهای چرب باید با غذا تأمین شوند، زیرا حیوانات نمی‌توانند خودشان آن‌ها را تولید کنند. ویتامین‌هایی مانند آ و د ۳ که به طور طبیعی در این محصول وجود دارند، متابولیسم استخوان و پوست را پشتیبانی می‌کنند. این محصول سرشار از آنتی‌اکسیدان‌های طبیعی است. روغن گردو، روغن دانه گل رز و روغن آرگان سرشار از ویتامین E هستند که از سلول‌ها محافظت و بازسازی می‌کند و رادیکال‌های آزاد و مضر را جذب می‌کند. ویتامین آ حاصل از روغن دانه گل رز تأثیر مثبتی بر پوست و غشاهای مخاطی دارد و به ویژه برای بینایی و رشد مهم است. روغن آرگان از دانه‌های درخت آرگان به دست می‌آید. روغن آرگان علاوه بر طیف متعادلی از اسیدهای چرب، حاوی مواد گیاهی ثانویه مانند فیتواسترول‌ها و اسکوالن است که تأثیر مثبتی بر متابولیسم سگ‌ها و گربه‌ها دارند. صرف نظر از نوع خوراک، ترکیب روغن دریایی پریمیوم تمام وعده‌های غذایی حیوان را بهبود می‌بخشد. این محصول از کمبود مواد مغذی در وعده‌های غذایی خام (ویتامین آ، ایی، د ۳ و اسیدهای چرب امگا ۳) جلوگیری می‌کند.", "description": "روغن دریایی پریمیوم، ترکیبی هماهنگ و ویژه از روغن‌های خوراکی با کیفیت بالا است که در اثرات مثبت خود بر کل ارگانیسم، یکدیگر را به طور بهینه تکمیل می‌کنند. روغن دریایی امگا ۳ طبیعی (کنترل شده با آفت‌کش) سرشار از اسیدهای چرب اشباع نشده EPA و DHA است که برای سگ‌ها و گربه‌ها ضروری هستند. این اسیدهای چرب باید با غذا تأمین شوند، زیرا حیوانات نمی‌توانند خودشان آن‌ها را تولید کنند. ویتامین‌هایی مانند آ و د ۳ که به طور طبیعی در این محصول وجود دارند، متابولیسم استخوان و پوست را پشتیبانی می‌کنند. این محصول سرشار از آنتی‌اکسیدان‌های طبیعی است. روغن گردو، روغن دانه گل رز و روغن آرگان سرشار از ویتامین E هستند که از سلول‌ها محافظت و بازسازی می‌کند و رادیکال‌های آزاد و مضر را جذب می‌کند. ویتامین آ حاصل از روغن دانه گل رز تأثیر مثبتی بر پوست و غشاهای مخاطی دارد و به ویژه برای بینایی و رشد مهم است. روغن آرگان از دانه‌های درخت آرگان به دست می‌آید. روغن آرگان علاوه بر طیف متعادلی از اسیدهای چرب، حاوی مواد گیاهی ثانویه مانند فیتواسترول‌ها و اسکوالن است که تأثیر مثبتی بر متابولیسم سگ‌ها و گربه‌ها دارند. صرف نظر از نوع خوراک، ترکیب روغن دریایی پریمیوم تمام وعده‌های غذایی حیوان را بهبود می‌بخشد. این محصول از کمبود مواد مغذی در وعده‌های غذایی خام (ویتامین آ، ایی، د ۳ و اسیدهای چرب امگا ۳) جلوگیری می‌کند.",
"short_description": "روغن دریایی امگا ۳ غنی از روغن آرگان و گل رز برای پوست و رشد.", "short_description": "روغن دریایی امگا ۳ غنی از روغن آرگان و گل رز برای پوست و رشد.",
@ -153,7 +153,7 @@
] ]
}, },
{ {
"name": "Canina Moortranke (عصاره پیت کانینا - مایع)", "name": "Canina Moortranke (عصاره پیت کنینا - مایع)",
"category_slug": "immune", "category_slug": "immune",
"description": "مواد هومیک موجود در عصاره پیت، از غشاهای مخاطی دستگاه گوارش محافظت کرده و از سیستم ایمنی روده، چه در حیوانات جوان و چه در حیوانات بالغ، پشتیبانی می‌کنند. عصاره پیت برای حیوانات خانگی یک ماده خوراکی طبیعی بدون افزودنی و بدون مواد نگهدارنده است. موارد استفاده: برای تحریک دستگاه گوارش، تغییر غلظت مدفوع، نفخ یا پس از درمان‌های آنتی‌بیوتیکی، برای تحریک اشتها، برای تقویت رشد و تقویت سیستم ایمنی، برای تقویت باروری در حیوانات نر و ماده، برای تحریک خون‌سازی و برای اتصال و حذف مواد مضر.", "description": "مواد هومیک موجود در عصاره پیت، از غشاهای مخاطی دستگاه گوارش محافظت کرده و از سیستم ایمنی روده، چه در حیوانات جوان و چه در حیوانات بالغ، پشتیبانی می‌کنند. عصاره پیت برای حیوانات خانگی یک ماده خوراکی طبیعی بدون افزودنی و بدون مواد نگهدارنده است. موارد استفاده: برای تحریک دستگاه گوارش، تغییر غلظت مدفوع، نفخ یا پس از درمان‌های آنتی‌بیوتیکی، برای تحریک اشتها، برای تقویت رشد و تقویت سیستم ایمنی، برای تقویت باروری در حیوانات نر و ماده، برای تحریک خون‌سازی و برای اتصال و حذف مواد مضر.",
"short_description": "عصاره طبیعی پیت غنی از اسید هومیک برای تنظیم فلور گوارشی.", "short_description": "عصاره طبیعی پیت غنی از اسید هومیک برای تنظیم فلور گوارشی.",
@ -303,9 +303,9 @@
] ]
}, },
{ {
"name": "Canina Rinderblut Pulver (پودر خون گاو کانینا)", "name": "Canina Rinderblut Pulver (پودر خون گاو کنینا)",
"category_slug": "nutrition", "category_slug": "nutrition",
"description": "پودر خون گاو کانینا منبع ارزشمندی از رنگدانه قرمز خون (هموگلوبین) است که حاوی آهن به عنوان اتم مرکزی است. آهن اکسیژن را به خون منتقل می‌کند. سایر مواد معدنی که به طور طبیعی در پودر خون گاو وجود دارند عبارتند از کلسیم، فسفر، سدیم، پتاسیم، منیزیم، مس، منگنز و روی. علاوه بر این، خون گاو حاوی پروتئین‌هایی است که به نوبه خود از اسیدهای آمینه تشکیل شده‌اند که برای رشد ماده ژنتیکی در هر سلول بدن مورد نیاز هستند. افزودن پودر خون گاو کانینا به ویژه برای سگ‌ها و گربه‌هایی که با گوشت تازه (B.A.R.F.) تغذیه می‌شوند توصیه می‌شود زیرا گوشت خام دیگر حاوی خون نیست. به دلیل محتوای پروتئین طبیعی بالا، پودر خون گاو کانینا باعث رشد عضلات می‌شود. پودر خون گاو کانینا از ایجاد شکل بدنی هماهنگ در طول مرحله رشد پشتیبانی می‌کند. سگ‌ها و گربه‌هایی که در حال گذراندن دوره نقاهت هستند یا تحت فشار خاصی قرار دارند از مصرف پودر خون گاو کانینا سود می‌برند: این پودر از قدرت، استقامت و رفاه پشتیبانی می‌کند و عملکرد را بهبود می‌بخشد.", "description": "پودر خون گاو کنینا منبع ارزشمندی از رنگدانه قرمز خون (هموگلوبین) است که حاوی آهن به عنوان اتم مرکزی است. آهن اکسیژن را به خون منتقل می‌کند. سایر مواد معدنی که به طور طبیعی در پودر خون گاو وجود دارند عبارتند از کلسیم، فسفر، سدیم، پتاسیم، منیزیم، مس، منگنز و روی. علاوه بر این، خون گاو حاوی پروتئین‌هایی است که به نوبه خود از اسیدهای آمینه تشکیل شده‌اند که برای رشد ماده ژنتیکی در هر سلول بدن مورد نیاز هستند. افزودن پودر خون گاو کنینا به ویژه برای سگ‌ها و گربه‌هایی که با گوشت تازه (B.A.R.F.) تغذیه می‌شوند توصیه می‌شود زیرا گوشت خام دیگر حاوی خون نیست. به دلیل محتوای پروتئین طبیعی بالا، پودر خون گاو کنینا باعث رشد عضلات می‌شود. پودر خون گاو کنینا از ایجاد شکل بدنی هماهنگ در طول مرحله رشد پشتیبانی می‌کند. سگ‌ها و گربه‌هایی که در حال گذراندن دوره نقاهت هستند یا تحت فشار خاصی قرار دارند از مصرف پودر خون گاو کنینا سود می‌برند: این پودر از قدرت، استقامت و رفاه پشتیبانی می‌کند و عملکرد را بهبود می‌بخشد.",
"short_description": "منبع آهن طبیعی هموگلوبین و اسیدهای آمینه برای سگ و گربه بارف.", "short_description": "منبع آهن طبیعی هموگلوبین و اسیدهای آمینه برای سگ و گربه بارف.",
"ingredients": "100% پودر خون گاو خشک شده.", "ingredients": "100% پودر خون گاو خشک شده.",
"usage_and_dosage": "0.3 گرم به ازای هر کیلوگرم وزن بدن روزانه، حداکثر 12 گرم.", "usage_and_dosage": "0.3 گرم به ازای هر کیلوگرم وزن بدن روزانه، حداکثر 12 گرم.",
@ -327,9 +327,9 @@
] ]
}, },
{ {
"name": "Canina Rinderfett Pulver (پودر چربی گوشت گاو کانینا)", "name": "Canina Rinderfett Pulver (پودر چربی گوشت گاو کنینا)",
"category_slug": "energy", "category_slug": "energy",
"description": "پودر چربی گوشت گاو کانینا یک منبع انرژی طبیعی از چربی گوشت گاو آب‌پز و آبگوشت حاصل از آن، بدون افزودن شکر است. سگ‌ها و گربه‌ها می‌توانند انرژی چربی را از این چربی حیوانی، منحصراً چربی گاو، تبدیل کرده و برای بدن خود استفاده کنند. حیوانات جوان در حال رشد که با وجود خوردن غذای کافی، لاغر هستند و وزن کم می‌کنند، بدنشان را برای نمایش آماده می‌کنند. حیوانات باردار و شیرده از کالری چربی سود می‌برند، همچنین در مورد تغذیه توله سگ‌هایشان. برای سگ‌هایی که از نظر جسمی بسیار فعال هستند، پودر چربی گوشت گاو کانینا باید به عنوان یک منبع انرژی طبیعی برای قدرت و استقامت در وعده‌های غذایی روزانه آنها گنجانده شود. پودر چربی گوشت گاو کانینا می‌تواند به حیوانات حساس به طعم و بدخوران کمک کند تا اشتهای خود را افزایش دهند زیرا چربی در انتقال طعم عالی است. علاوه بر این، پودر چربی گوشت گاو کانینا یک منبع عالی چربی حیوانی برای بهینه‌سازی وعده‌های غذایی خام است.", "description": "پودر چربی گوشت گاو کنینا یک منبع انرژی طبیعی از چربی گوشت گاو آب‌پز و آبگوشت حاصل از آن، بدون افزودن شکر است. سگ‌ها و گربه‌ها می‌توانند انرژی چربی را از این چربی حیوانی، منحصراً چربی گاو، تبدیل کرده و برای بدن خود استفاده کنند. حیوانات جوان در حال رشد که با وجود خوردن غذای کافی، لاغر هستند و وزن کم می‌کنند، بدنشان را برای نمایش آماده می‌کنند. حیوانات باردار و شیرده از کالری چربی سود می‌برند، همچنین در مورد تغذیه توله سگ‌هایشان. برای سگ‌هایی که از نظر جسمی بسیار فعال هستند، پودر چربی گوشت گاو کنینا باید به عنوان یک منبع انرژی طبیعی برای قدرت و استقامت در وعده‌های غذایی روزانه آنها گنجانده شود. پودر چربی گوشت گاو کنینا می‌تواند به حیوانات حساس به طعم و بدخوران کمک کند تا اشتهای خود را افزایش دهند زیرا چربی در انتقال طعم عالی است. علاوه بر این، پودر چربی گوشت گاو کنینا یک منبع عالی چربی حیوانی برای بهینه‌سازی وعده‌های غذایی خام است.",
"short_description": "پودر چربی طبیعی گاو جهت افزایش سریع وزن و اشتهاآور پت‌های بدخوراک.", "short_description": "پودر چربی طبیعی گاو جهت افزایش سریع وزن و اشتهاآور پت‌های بدخوراک.",
"ingredients": "72% چربی گوشت گاو خشک شده، 28% عصاره گوشت گاو.", "ingredients": "72% چربی گوشت گاو خشک شده، 28% عصاره گوشت گاو.",
"usage_and_dosage": "نصف قاشق چایخوری به ازای هر 10 کیلوگرم وزن بدن، حداکثر 3 قاشق چایخوری روزانه.", "usage_and_dosage": "نصف قاشق چایخوری به ازای هر 10 کیلوگرم وزن بدن، حداکثر 3 قاشق چایخوری روزانه.",
@ -344,9 +344,9 @@
] ]
}, },
{ {
"name": "Canina Schwarz Kummel Samen (سیاه دانه مصری کانینا)", "name": "Canina Schwarz Kummel Samen (سیاه دانه مصری کنینا)",
"category_slug": "immune", "category_slug": "immune",
"description": "دانه‌های زیره سیاه کانینا غنی از اسیدهای چرب غیراشباع چندگانه مانند اسید لینولئیک هستند و همچنین حاوی روغن‌های ضروری و مواد تلخ مانند نیژلین می‌باشند. آنها می‌توانند تأثیر تنظیم‌کننده‌ای بر متابولیسم و هضم داشته باشند. آنها می‌توانند تأثیر مثبتی بر سلامت و عملکرد سگ شما داشته باشند و سیستم دفاعی بدن را تقویت کنند. به دلیل عطر تند و آجیلی، پذیرش دانه‌های زیره سیاه کانینا عموماً بسیار خوب است. برای سگ‌های باردار و شیرده استفاده نشود.", "description": "دانه‌های زیره سیاه کنینا غنی از اسیدهای چرب غیراشباع چندگانه مانند اسید لینولئیک هستند و همچنین حاوی روغن‌های ضروری و مواد تلخ مانند نیژلین می‌باشند. آنها می‌توانند تأثیر تنظیم‌کننده‌ای بر متابولیسم و هضم داشته باشند. آنها می‌توانند تأثیر مثبتی بر سلامت و عملکرد سگ شما داشته باشند و سیستم دفاعی بدن را تقویت کنند. به دلیل عطر تند و آجیلی، پذیرش دانه‌های زیره سیاه کنینا عموماً بسیار خوب است. برای سگ‌های باردار و شیرده استفاده نشود.",
"short_description": "دانه‌های سیاه دانه مصری جهت تقویت سیستم ایمنی و دفع انگل‌های گوارشی.", "short_description": "دانه‌های سیاه دانه مصری جهت تقویت سیستم ایمنی و دفع انگل‌های گوارشی.",
"ingredients": "100% دانه های سیاه دانه مصری.", "ingredients": "100% دانه های سیاه دانه مصری.",
"usage_and_dosage": "1 قاشق چایخوری به ازای هر 10 کیلوگرم وزن بدن روزانه، حداکثر 4 قاشق چایخوری.", "usage_and_dosage": "1 قاشق چایخوری به ازای هر 10 کیلوگرم وزن بدن روزانه، حداکثر 4 قاشق چایخوری.",
@ -361,7 +361,7 @@
] ]
}, },
{ {
"name": "Canina Seelgen Tabletten (قرص جلبک دریایی کانینا)", "name": "Canina Seelgen Tabletten (قرص جلبک دریایی کنینا)",
"category_slug": "special-care", "category_slug": "special-care",
"description": "این محصول از جلبک دریایی تازه (از کشت ارگانیک کنترل شده) به دست می‌آید. با توجه به محتوای بالای مواد معدنی، اسیدهای آمینه و عناصر کمیاب موجود در این محصول، این محصول طبیعی با کیفیت بالا، تغذیه نامتعادل را جبران کرده و از طریق محتوای ید، روی و کاروتنوئید خود، متابولیسم پوست و رنگدانه‌های پوشش، بینی و پنجه‌ها را پشتیبانی می‌کند. اسید آمینه لیزین موجود در این محصول ضریب تبدیل غذایی را بهینه می‌کند؛ سلولز و مانیتول هضم را پشتیبانی می‌کنند. محتوای بالای ویتامین‌ها (به عنوان مثال ویتامین سی) سیستم ایمنی را تقویت کرده و تناسب اندام، انگیزه و رفاه را افزایش می‌دهد.", "description": "این محصول از جلبک دریایی تازه (از کشت ارگانیک کنترل شده) به دست می‌آید. با توجه به محتوای بالای مواد معدنی، اسیدهای آمینه و عناصر کمیاب موجود در این محصول، این محصول طبیعی با کیفیت بالا، تغذیه نامتعادل را جبران کرده و از طریق محتوای ید، روی و کاروتنوئید خود، متابولیسم پوست و رنگدانه‌های پوشش، بینی و پنجه‌ها را پشتیبانی می‌کند. اسید آمینه لیزین موجود در این محصول ضریب تبدیل غذایی را بهینه می‌کند؛ سلولز و مانیتول هضم را پشتیبانی می‌کنند. محتوای بالای ویتامین‌ها (به عنوان مثال ویتامین سی) سیستم ایمنی را تقویت کرده و تناسب اندام، انگیزه و رفاه را افزایش می‌دهد.",
"short_description": "قرص جلبک دریایی ارگانیک جهت تیره‌سازی پیگمنت بینی، پنجه و براقیت مو.", "short_description": "قرص جلبک دریایی ارگانیک جهت تیره‌سازی پیگمنت بینی، پنجه و براقیت مو.",
@ -385,7 +385,7 @@
] ]
}, },
{ {
"name": "Canina Bio-Seelgenmehl (پودر جلبک دریایی ارگانیک کانینا)", "name": "Canina Bio-Seelgenmehl (پودر جلبک دریایی ارگانیک کنینا)",
"category_slug": "special-care", "category_slug": "special-care",
"description": "این محصول از جلبک دریایی تازه (از کشت ارگانیک کنترل شده) به دست می‌آید. با توجه به محتوای بالای مواد معدنی، اسیدهای آمینه و عناصر کمیاب موجود در این محصول، این محصول طبیعی با کیفیت بالا، تغذیه نامتعادل را جبران کرده و از طریق محتوای ید، روی و کاروتنوئید خود، متابولیسم پوست و رنگدانه‌های پوشش، بینی و پنجه‌ها را پشتیبانی می‌کند. اسید آمینه لیزین موجود در این محصول ضریب تبدیل غذایی را بهینه می‌کند؛ سلولز و مانیتول هضم را پشتیبانی می‌کنند. محتوای بالای ویتامین‌ها (به عنوان مثال ویتامین سی) سیستم ایمنی را تقویت کرده و تناسب اندام، انگیزه و رفاه را افزایش می‌دهد.", "description": "این محصول از جلبک دریایی تازه (از کشت ارگانیک کنترل شده) به دست می‌آید. با توجه به محتوای بالای مواد معدنی، اسیدهای آمینه و عناصر کمیاب موجود در این محصول، این محصول طبیعی با کیفیت بالا، تغذیه نامتعادل را جبران کرده و از طریق محتوای ید، روی و کاروتنوئید خود، متابولیسم پوست و رنگدانه‌های پوشش، بینی و پنجه‌ها را پشتیبانی می‌کند. اسید آمینه لیزین موجود در این محصول ضریب تبدیل غذایی را بهینه می‌کند؛ سلولز و مانیتول هضم را پشتیبانی می‌کنند. محتوای بالای ویتامین‌ها (به عنوان مثال ویتامین سی) سیستم ایمنی را تقویت کرده و تناسب اندام، انگیزه و رفاه را افزایش می‌دهد.",
"short_description": "پودر خالص جلبک دریایی ارگانیک برای تقویت پیگمنت‌های تیره و غده تیروئید.", "short_description": "پودر خالص جلبک دریایی ارگانیک برای تقویت پیگمنت‌های تیره و غده تیروئید.",
@ -402,7 +402,7 @@
] ]
}, },
{ {
"name": "Canina TAURIN Fur Katzen (پودر تورین مخصوص گربه‌ها کانینا)", "name": "Canina TAURIN Fur Katzen (پودر تورین مخصوص گربه‌ها کنینا)",
"category_slug": "special-care", "category_slug": "special-care",
"description": "تورین یک اسید آمینه مبتنی بر گوگرد است که در تمام سلول‌های بدن حیوانات یافت می‌شود. گربه‌ها نمی‌توانند خود تورین تولید کنند. تورین برای آنها ضروری (حیاتی) است و بنابراین باید با غذا تأمین شود. تورین دارای اثر سوخت و ساز است، سیستم ایمنی را تقویت می‌کند، از بینایی و عملکرد قلب پشتیبانی می‌کند و پوست و پوشش را بهبود می‌بخشد. مواد معدنی و آنزیم‌های ارزشمندی را فراهم می‌کند، مخمر آبجو موجود در این محصول، ویتامین‌های گروه بی که تأثیر مثبتی بر سوخت و ساز بدن دارند و سیستم ایمنی را تقویت می‌کنند.", "description": "تورین یک اسید آمینه مبتنی بر گوگرد است که در تمام سلول‌های بدن حیوانات یافت می‌شود. گربه‌ها نمی‌توانند خود تورین تولید کنند. تورین برای آنها ضروری (حیاتی) است و بنابراین باید با غذا تأمین شود. تورین دارای اثر سوخت و ساز است، سیستم ایمنی را تقویت می‌کند، از بینایی و عملکرد قلب پشتیبانی می‌کند و پوست و پوشش را بهبود می‌بخشد. مواد معدنی و آنزیم‌های ارزشمندی را فراهم می‌کند، مخمر آبجو موجود در این محصول، ویتامین‌های گروه بی که تأثیر مثبتی بر سوخت و ساز بدن دارند و سیستم ایمنی را تقویت می‌کنند.",
"short_description": "پودر تورین ۱۰۰٪ خالص و مخمر آبجو برای سلامت قلب و بینایی گربه‌ها.", "short_description": "پودر تورین ۱۰۰٪ خالص و مخمر آبجو برای سلامت قلب و بینایی گربه‌ها.",
@ -419,7 +419,7 @@
] ]
}, },
{ {
"name": "Canina Velox Gelenkenergie (ولوکس ژلنک‌انرژی کانینا)", "name": "Canina Velox Gelenkenergie (ولوکس ژلنک‌انرژی کنینا)",
"category_slug": "joints", "category_slug": "joints",
"description": "قدرت متمرکز صدف لب سبز نیوزیلندی برای سیستم اسکلتی عضلانی و عملکرد دوست چهارپای شما! این محصول برای تأمین با کیفیت بالای گلیکوزآمینوگلیکان‌ها به سیستم اسکلتی عضلانی استفاده می‌شود که در صدف لب سبز نیوزیلندی موجود است. گگ‌ها، مانند سولفات کندرویتین، سولفات هپاران و اسید هیالورونیک، ساختارهای بافت همبند مانند تاندون‌ها، رباط‌ها، غضروف مفاصل و دیسک‌های بین مهره‌ای را تثبیت و بازسازی می‌کنند. عملکرد دوست چهارپای شما بهبود می‌یابد و در نتیجه سلامت آنها را ارتقا می‌دهد. اسیدهای چرب امگا طبیعی به طور کلی سهم ارزشمندی در متابولیسم کلی و به طور خاص در متابولیسم مفاصل دارند. ظرفیت بافری غضروف مفصلی در عملکرد فیزیولوژیکی آن پشتیبانی می‌شود.", "description": "قدرت متمرکز صدف لب سبز نیوزیلندی برای سیستم اسکلتی عضلانی و عملکرد دوست چهارپای شما! این محصول برای تأمین با کیفیت بالای گلیکوزآمینوگلیکان‌ها به سیستم اسکلتی عضلانی استفاده می‌شود که در صدف لب سبز نیوزیلندی موجود است. گگ‌ها، مانند سولفات کندرویتین، سولفات هپاران و اسید هیالورونیک، ساختارهای بافت همبند مانند تاندون‌ها، رباط‌ها، غضروف مفاصل و دیسک‌های بین مهره‌ای را تثبیت و بازسازی می‌کنند. عملکرد دوست چهارپای شما بهبود می‌یابد و در نتیجه سلامت آنها را ارتقا می‌دهد. اسیدهای چرب امگا طبیعی به طور کلی سهم ارزشمندی در متابولیسم کلی و به طور خاص در متابولیسم مفاصل دارند. ظرفیت بافری غضروف مفصلی در عملکرد فیزیولوژیکی آن پشتیبانی می‌شود.",
"short_description": "پودر ۱۰۰٪ خالص صدف لب‌سبز نیوزیلندی برای بازسازی مفاصل و تاندون‌ها.", "short_description": "پودر ۱۰۰٪ خالص صدف لب‌سبز نیوزیلندی برای بازسازی مفاصل و تاندون‌ها.",
@ -436,9 +436,9 @@
] ]
}, },
{ {
"name": "Canina Welpenbrei (سرلاک توله سگ - ولپن‌برای کانینا)", "name": "Canina Welpenbrei (سرلاک توله سگ - ولپن‌برای کنینا)",
"category_slug": "nutrition", "category_slug": "nutrition",
"description": "فرنی توله سگ کانینا پرورش سالم توله سگ‌های شما را ارتقا می‌دهد. طیف گسترده‌ای از پروتئین‌ها، ترکیب مناسب مواد مغذی و فیبر و محتوای بالای مواد معدنی، عناصر کمیاب و ویتامین‌ها، این خوراک را به یک محصول برتر تبدیل می‌کند. از آنجایی که فقط شیر توله سگ کانینا باید در طول ۳ هفته اول به توله سگ‌ها داده شود، فرنی توله سگ کانینا را می‌توان از این مرحله شروع کرد. توله سگ‌ها هنوز باید شیر توله سگ دریافت کنند و این مقدار فقط باید از ۶ هفتگی کاهش یابد. رشد بهینه دندان‌ها و سیستم اسکلتی تضمین شود. تجویز همزمان ویتامین دی ۲ مجاز نیست.", "description": "فرنی توله سگ کنینا پرورش سالم توله سگ‌های شما را ارتقا می‌دهد. طیف گسترده‌ای از پروتئین‌ها، ترکیب مناسب مواد مغذی و فیبر و محتوای بالای مواد معدنی، عناصر کمیاب و ویتامین‌ها، این خوراک را به یک محصول برتر تبدیل می‌کند. از آنجایی که فقط شیر توله سگ کنینا باید در طول ۳ هفته اول به توله سگ‌ها داده شود، فرنی توله سگ کنینا را می‌توان از این مرحله شروع کرد. توله سگ‌ها هنوز باید شیر توله سگ دریافت کنند و این مقدار فقط باید از ۶ هفتگی کاهش یابد. رشد بهینه دندان‌ها و سیستم اسکلتی تضمین شود. تجویز همزمان ویتامین دی ۲ مجاز نیست.",
"short_description": "غذای کمکی توله سگ از ۳ هفتگی سرشار از کلسیم برای رشد اسکلتی.", "short_description": "غذای کمکی توله سگ از ۳ هفتگی سرشار از کلسیم برای رشد اسکلتی.",
"ingredients": "غلات، گوشت و فرآورده های فرعی حیوانی، فرآورده های فرعی گیاهی، شیر و محصولات لبنی، مخمر.", "ingredients": "غلات، گوشت و فرآورده های فرعی حیوانی، فرآورده های فرعی گیاهی، شیر و محصولات لبنی، مخمر.",
"usage_and_dosage": "1 قسمت فرنی با 3 قسمت آب گرم مخلوط شود. از سن 3 تا 6 هفتگی.", "usage_and_dosage": "1 قسمت فرنی با 3 قسمت آب گرم مخلوط شود. از سن 3 تا 6 هفتگی.",
@ -453,7 +453,7 @@
] ]
}, },
{ {
"name": "Canina Welpenmilch (شیر خشک توله سگ کانینا)", "name": "Canina Welpenmilch (شیر خشک توله سگ کنینا)",
"category_slug": "nutrition", "category_slug": "nutrition",
"description": "شیر مخصوص توله سگ با اجزای سازنده بیولوژیکی ارزشمند: انرژی و قدرت تغذیه با تمام ویتامین‌ها و مواد معدنی لازم برای رشد سالم. جایگزین شیر مادر برای توله سگ‌ها از ۱ روزگی، برای تغذیه توله سگ‌های بی مادر - اگر تولید شیر مادر کافی نیست - برای توله‌های بزرگ - برای آماده‌سازی و در طول شیردهی - به عنوان مکمل برای سگ‌های کار و پرورش - به عنوان مکمل برای سگ‌های مسن و بیمار. طرز تهیه: بر اساس شیر طبیعی سگ مادر، نوشیدنی‌های شیر را با ۴ قسمت وزنی آب (۸۰٪) و ۱ قسمت وزنی شیر توله سگ (۲۰٪) آماده کنید.", "description": "شیر مخصوص توله سگ با اجزای سازنده بیولوژیکی ارزشمند: انرژی و قدرت تغذیه با تمام ویتامین‌ها و مواد معدنی لازم برای رشد سالم. جایگزین شیر مادر برای توله سگ‌ها از ۱ روزگی، برای تغذیه توله سگ‌های بی مادر - اگر تولید شیر مادر کافی نیست - برای توله‌های بزرگ - برای آماده‌سازی و در طول شیردهی - به عنوان مکمل برای سگ‌های کار و پرورش - به عنوان مکمل برای سگ‌های مسن و بیمار. طرز تهیه: بر اساس شیر طبیعی سگ مادر، نوشیدنی‌های شیر را با ۴ قسمت وزنی آب (۸۰٪) و ۱ قسمت وزنی شیر توله سگ (۲۰٪) آماده کنید.",
"short_description": "شیر خشک غنی از پروتئین جایگزین کامل شیر مادر مخصوص توله سگ‌ها.", "short_description": "شیر خشک غنی از پروتئین جایگزین کامل شیر مادر مخصوص توله سگ‌ها.",
@ -494,9 +494,9 @@
] ]
}, },
{ {
"name": "Canina Mikrosilber Zahngel (ژل دندان میکروسیلور کانینا)", "name": "Canina Mikrosilber Zahngel (ژل دندان میکروسیلور کنینا)",
"category_slug": "special-care", "category_slug": "special-care",
"description": "ژل دندانی میکروسیلور کانینا با عصاره دانه گریپ فروت مشکلات اصلی بهداشت دهان و دندان در سگ‌ها و گربه‌ها، مانند پلاک، جرم و التهاب لثه را برطرف می‌کند. اثر تمیزکنندگی دندان بر اساس فرمولاسیونی از میکروسیلور و عصاره دانه گریپ فروت است که به خوبی تحمل می‌شود. در صورت استفاده منظم، تأثیر واقعی بر تشکیل جرم جدید دارد. مصرف آن بی خطر است. بیوفلاونوئیدهای موجود در عصاره دانه گریپ فروت دارای اثر آنتی‌اکسیدانی هستند. آنها به محافظت از دندان‌ها در برابر حملات باکتری‌های پلاک کمک می‌کنند. ویتامین سی موجود در این محصول از سلامت لثه‌ها پشتیبانی می‌کند و اسیدهای آمینه و چرب عصاره دانه گریپ فروت، حل شدن پلاک دندان را تقویت می‌کنند.", "description": "ژل دندانی میکروسیلور کنینا با عصاره دانه گریپ فروت مشکلات اصلی بهداشت دهان و دندان در سگ‌ها و گربه‌ها، مانند پلاک، جرم و التهاب لثه را برطرف می‌کند. اثر تمیزکنندگی دندان بر اساس فرمولاسیونی از میکروسیلور و عصاره دانه گریپ فروت است که به خوبی تحمل می‌شود. در صورت استفاده منظم، تأثیر واقعی بر تشکیل جرم جدید دارد. مصرف آن بی خطر است. بیوفلاونوئیدهای موجود در عصاره دانه گریپ فروت دارای اثر آنتی‌اکسیدانی هستند. آنها به محافظت از دندان‌ها در برابر حملات باکتری‌های پلاک کمک می‌کنند. ویتامین سی موجود در این محصول از سلامت لثه‌ها پشتیبانی می‌کند و اسیدهای آمینه و چرب عصاره دانه گریپ فروت، حل شدن پلاک دندان را تقویت می‌کنند.",
"short_description": "ژل دندان تخصصی برای از بین بردن پلاک، تارتار و بوی بد دهان.", "short_description": "ژل دندان تخصصی برای از بین بردن پلاک، تارتار و بوی بد دهان.",
"ingredients": "آب، پلی گلیسریل-4-کاپرات، کربومر، نقره میکروسیلور، عصاره دانه گریپ فروت، ویتامین C.", "ingredients": "آب، پلی گلیسریل-4-کاپرات، کربومر، نقره میکروسیلور، عصاره دانه گریپ فروت، ویتامین C.",
"usage_and_dosage": "روزانه یک بار ترجیحا عصرها روی لثه و قسمت بیرونی دندان ها مالیده شود.", "usage_and_dosage": "روزانه یک بار ترجیحا عصرها روی لثه و قسمت بیرونی دندان ها مالیده شود.",
@ -513,7 +513,7 @@
{ {
"name": "Canina Nova Gard Green Augenpflege (قطره تمیزکننده چشم نوواگارد گرین)", "name": "Canina Nova Gard Green Augenpflege (قطره تمیزکننده چشم نوواگارد گرین)",
"category_slug": "special-care", "category_slug": "special-care",
"description": "مراقبت صحیح از چشم‌ها و گوش‌ها برای سلامت و رفاه سگ‌ها و گربه‌ها بسیار مهم است. آبریزش یا گرفتگی چشم، مانند خارش گوش‌ها، به ویژه در گربه‌ها به طور کلی و در سگ‌هایی با گوش‌های افتاده، یک مشکل روزمره است. پیشگیری با یک محصول مراقبتی طبیعی، ملایم و مؤثر که به طور مؤثر از درمان دامپزشکی نیز پشتیبانی می‌کند. مراقبت از چشم کانینا (برای سگ‌ها و گربه‌ها) چشم‌های گرفته/گریان حیوانات (به علاوه ناحیه چشم) را تمیز و مراقبت می‌کند. با چکاندن قطره در چشم، چشم‌های تحریک شده تسکین می‌یابند. این محصول را می‌توان برای مراقبت روزانه از چشم استفاده کرد و از چسبندگی و سوزش مجدد جلوگیری می‌کند. استفاده نوآورانه از نقره کلوئیدی از التهاب جلوگیری می‌کند.", "description": "مراقبت صحیح از چشم‌ها و گوش‌ها برای سلامت و رفاه سگ‌ها و گربه‌ها بسیار مهم است. آبریزش یا گرفتگی چشم، مانند خارش گوش‌ها، به ویژه در گربه‌ها به طور کلی و در سگ‌هایی با گوش‌های افتاده، یک مشکل روزمره است. پیشگیری با یک محصول مراقبتی طبیعی، ملایم و مؤثر که به طور مؤثر از درمان دامپزشکی نیز پشتیبانی می‌کند. مراقبت از چشم کنینا (برای سگ‌ها و گربه‌ها) چشم‌های گرفته/گریان حیوانات (به علاوه ناحیه چشم) را تمیز و مراقبت می‌کند. با چکاندن قطره در چشم، چشم‌های تحریک شده تسکین می‌یابند. این محصول را می‌توان برای مراقبت روزانه از چشم استفاده کرد و از چسبندگی و سوزش مجدد جلوگیری می‌کند. استفاده نوآورانه از نقره کلوئیدی از التهاب جلوگیری می‌کند.",
"short_description": "قطره استریل حاوی نقره کلوئیدی جهت رفع التهاب و ترشحات چشم پت.", "short_description": "قطره استریل حاوی نقره کلوئیدی جهت رفع التهاب و ترشحات چشم پت.",
"ingredients": "آب، هیالورونات سدیم، نقره کلوئیدی، رتینول پالمیتات.", "ingredients": "آب، هیالورونات سدیم، نقره کلوئیدی، رتینول پالمیتات.",
"usage_and_dosage": "چند قطره روزانه روی کیسه ملتحمه چشم ریخته شود. اطراف چشم تمیز شود.", "usage_and_dosage": "چند قطره روزانه روی کیسه ملتحمه چشم ریخته شود. اطراف چشم تمیز شود.",

View File

@ -455,16 +455,16 @@ export async function main() {
// 3. Seed some Blogs // 3. Seed some Blogs
const blog1Content = [ const blog1Content = [
'<p><strong>تغذیه صحیح سگ‌های بالغ</strong> یکی از مهم‌ترین عوامل در حفظ سلامت و افزایش طول عمر آن‌هاست. برند کانینا (Canina) با بیش از ۴۰ سال سابقه در تولید مکمل‌های دامپزشکی در آلمان، استاندارد طلایی تغذیه تکمیلی را تعریف کرده است.</p>', '<p><strong>تغذیه صحیح سگ‌های بالغ</strong> یکی از مهم‌ترین عوامل در حفظ سلامت و افزایش طول عمر آن‌هاست. برند کنینا (Canina) با بیش از ۴۰ سال سابقه در تولید مکمل‌های دامپزشکی در آلمان، استاندارد طلایی تغذیه تکمیلی را تعریف کرده است.</p>',
'', '',
'<h2>اصول تغذیه سگ بالغ</h2>', '<h2>اصول تغذیه سگ بالغ</h2>',
'<p>سگ‌های بالغ به رژیم غذایی متعادل حاوی پروتئین، چربی، کربوهیدرات، ویتامین‌ها و مواد معدنی نیاز دارند. نسبت Ca:P (کلسیم به فسفر) باید در محدوده ۱.۲:۱ تا ۱.۵:۱ باشد. هرگونه عدم تعادل در این نسبت می‌تواند منجر به مشکلات کلیوی و استخوانی شود.</p>', '<p>سگ‌های بالغ به رژیم غذایی متعادل حاوی پروتئین، چربی، کربوهیدرات، ویتامین‌ها و مواد معدنی نیاز دارند. نسبت Ca:P (کلسیم به فسفر) باید در محدوده ۱.۲:۱ تا ۱.۵:۱ باشد. هرگونه عدم تعادل در این نسبت می‌تواند منجر به مشکلات کلیوی و استخوانی شود.</p>',
'', '',
'<h2>نقش مکمل‌ها در رژیم غذایی</h2>', '<h2>نقش مکمل‌ها در رژیم غذایی</h2>',
'<p>حتی با بهترین رژیم غذایی، ممکن است برخی مواد مغذی به اندازه کافی تأمین نشوند. محصولات کانینا مانند <strong>Petvital Vitamin-Tabs</strong> (کمپلکس کامل A تا K3) و <strong>Biotin-Tabs</strong> (۱ میلیون میکروگرم بیوتین) نیازهای تغذیه‌ای را پوشش می‌دهند.</p>', '<p>حتی با بهترین رژیم غذایی، ممکن است برخی مواد مغذی به اندازه کافی تأمین نشوند. محصولات کنینا مانند <strong>Petvital Vitamin-Tabs</strong> (کمپلکس کامل A تا K3) و <strong>Biotin-Tabs</strong> (۱ میلیون میکروگرم بیوتین) نیازهای تغذیه‌ای را پوشش می‌دهند.</p>',
'', '',
'<h2>اسیدهای چرب ضروری</h2>', '<h2>اسیدهای چرب ضروری</h2>',
'<p>روغن سالمون کانینا (Lachs-Öl) با ۱۴٪ امگا ۳ (EPA/DHA) به روش Cold Press استخراج می‌شود و تمام ویتامین‌های حساس به حرارت را حفظ می‌کند. EPA پیش‌ماده رزولوین‌های ضدالتهابی و DHA ۳۰٪ وزن خشک مغز را تشکیل می‌دهد.</p>', '<p>روغن سالمون کنینا (Lachs-Öl) با ۱۴٪ امگا ۳ (EPA/DHA) به روش Cold Press استخراج می‌شود و تمام ویتامین‌های حساس به حرارت را حفظ می‌کند. EPA پیش‌ماده رزولوین‌های ضدالتهابی و DHA ۳۰٪ وزن خشک مغز را تشکیل می‌دهد.</p>',
'', '',
'<h2>مراقبت از مفاصل</h2>', '<h2>مراقبت از مفاصل</h2>',
'<p>با افزایش سن سگ، غضروف مفصلی تحلیل می‌رود. گلیکوزآمینوگلیکان‌های صدف لب سبز (GAG) با جذب مولکول‌های آب در ماتریکس غضروفی، خاصیت بافری مفاصل را بازیابی می‌کنند. <strong>CANHYDROX GAG</strong> و <strong>FLEXAN</strong> حاوی این ترکیبات حیاتی هستند.</p>', '<p>با افزایش سن سگ، غضروف مفصلی تحلیل می‌رود. گلیکوزآمینوگلیکان‌های صدف لب سبز (GAG) با جذب مولکول‌های آب در ماتریکس غضروفی، خاصیت بافری مفاصل را بازیابی می‌کنند. <strong>CANHYDROX GAG</strong> و <strong>FLEXAN</strong> حاوی این ترکیبات حیاتی هستند.</p>',
@ -479,12 +479,12 @@ export async function main() {
].join('\n'); ].join('\n');
const blog2Content = [ const blog2Content = [
'<p><strong>کانینا (Canina)</strong> نماینده رسمی برند <strong>Canina pharma GmbH</strong> در ایران است. این شرکت آلمانی در سال ۱۹۸۴ توسط <strong>پتر و کریستین کرشن‌مایر</strong> در شهر برگیش گلادباخ تأسیس شد و به یکی از پیشگامان تولید مکمل‌های طبیعی دامپزشکی در اروپا تبدیل شده است.</p>', '<p><strong>کنینا (Canina)</strong> نماینده رسمی برند <strong>Canina pharma GmbH</strong> در ایران است. این شرکت آلمانی در سال ۱۹۸۴ توسط <strong>پتر و کریستین کرشن‌مایر</strong> در شهر برگیش گلادباخ تأسیس شد و به یکی از پیشگامان تولید مکمل‌های طبیعی دامپزشکی در اروپا تبدیل شده است.</p>',
'', '',
'<h2>فلسفه Canina: "مادر طبیعت بهترین نسخه را می‌داند"</h2>', '<h2>فلسفه Canina: "مادر طبیعت بهترین نسخه را می‌داند"</h2>',
'<p>تمام محصولات کانینا بر پایه مواد اولیه طبیعی و با فرآیندهای پیشرفته تولید می‌شوند. این شرکت دارای گواهی‌های معتبر <strong>IFS (International Featured Standard)</strong> و <strong>HACCP</strong> است و محصولات آن در بیش از ۴۰ کشور جهان توزیع می‌شود.</p>', '<p>تمام محصولات کنینا بر پایه مواد اولیه طبیعی و با فرآیندهای پیشرفته تولید می‌شوند. این شرکت دارای گواهی‌های معتبر <strong>IFS (International Featured Standard)</strong> و <strong>HACCP</strong> است و محصولات آن در بیش از ۴۰ کشور جهان توزیع می‌شود.</p>',
'', '',
'<h2>چرا مکمل‌های کانینا در ایران؟</h2>', '<h2>چرا مکمل‌های کنینا در ایران؟</h2>',
'<ul>', '<ul>',
'<li><strong>کیفیت آلمانی:</strong> تمام محصولات در کارخانه برگیش گلادباخ تحت نظارت دامپزشکان تولید می‌شوند</li>', '<li><strong>کیفیت آلمانی:</strong> تمام محصولات در کارخانه برگیش گلادباخ تحت نظارت دامپزشکان تولید می‌شوند</li>',
'<li><strong>بدون مواد مصنوعی:</strong> فاقد رنگ، طعم و نگهدارنده مصنوعی</li>', '<li><strong>بدون مواد مصنوعی:</strong> فاقد رنگ، طعم و نگهدارنده مصنوعی</li>',
@ -499,7 +499,7 @@ export async function main() {
'<p><strong>قلب و انرژی:</strong> Herz Vital (ال-کارنیتین و زالزالک)، Energy-Gel (ویتامین‌های گروه B)</p>', '<p><strong>قلب و انرژی:</strong> Herz Vital (ال-کارنیتین و زالزالک)، Energy-Gel (ویتامین‌های گروه B)</p>',
'', '',
'<h2>تعهد به سلامت حیوانات</h2>', '<h2>تعهد به سلامت حیوانات</h2>',
'<p>کانینا به عنوان نماینده رسمی کانینا در ایران، متعهد به ارائه محصولات اصل با ضمانت اصالت و پروانه دامپزشکی است. تمام محصولات دارای بسته‌بندی اصلی آلمان و بارکد اختصاصی هستند.</p>', '<p>کنینا به عنوان نماینده رسمی کنینا در ایران، متعهد به ارائه محصولات اصل با ضمانت اصالت و پروانه دامپزشکی است. تمام محصولات دارای بسته‌بندی اصلی آلمان و بارکد اختصاصی هستند.</p>',
].join('\n'); ].join('\n');
const blogs = [ const blogs = [
@ -511,7 +511,7 @@ export async function main() {
isPublished: true, isPublished: true,
}, },
{ {
title: 'چرا مکمل‌های کانینا؟', title: 'چرا مکمل‌های کنینا؟',
slug: 'why-canino-supplements', slug: 'why-canino-supplements',
content: blog2Content, content: blog2Content,
authorId: adminId, authorId: adminId,

View File

@ -24,7 +24,7 @@ const UI_TEXTS: Record<string, string> = {
"nav_pet_switcher_title": "تغییر پت یا مدیریت حساب", "nav_pet_switcher_title": "تغییر پت یا مدیریت حساب",
"nav_register_pet": "ثبت همدم (Pet) جدید", "nav_register_pet": "ثبت همدم (Pet) جدید",
"nav_wallet_orders": "کیف پول و سفارشات", "nav_wallet_orders": "کیف پول و سفارشات",
"nav_logout": "خروج از حساب کانینا", "nav_logout": "خروج از حساب کنینا",
"nav_login": "ورود / ثبت‌نام", "nav_login": "ورود / ثبت‌نام",
"nav_cart": "سبد خرید", "nav_cart": "سبد خرید",
@ -44,7 +44,7 @@ const UI_TEXTS: Record<string, string> = {
// === Smart Advisor === // === Smart Advisor ===
"advisor_badge": "تکنولوژی پایش هوشمند", "advisor_badge": "تکنولوژی پایش هوشمند",
"advisor_title": "دستیار سلامت کانینا", "advisor_title": "دستیار سلامت کنینا",
"advisor_subtitle": "فقط چند قدم تا صدور شناسنامه هوشمند و دریافت پیشنهادات تخصصی", "advisor_subtitle": "فقط چند قدم تا صدور شناسنامه هوشمند و دریافت پیشنهادات تخصصی",
"advisor_step1_title": "گام اول: هویت بصری", "advisor_step1_title": "گام اول: هویت بصری",
"advisor_step1_desc": "همدم شما رو با چه اسمی صدا می‌زنید؟", "advisor_step1_desc": "همدم شما رو با چه اسمی صدا می‌زنید؟",
@ -69,7 +69,7 @@ const UI_TEXTS: Record<string, string> = {
"featured_badge": "بیشترین انتخاب توسط دامپزشکان", "featured_badge": "بیشترین انتخاب توسط دامپزشکان",
"featured_title_line1": "محصولات برگزیده و راهکارهای", "featured_title_line1": "محصولات برگزیده و راهکارهای",
"featured_title_line2": "درمان تخصصی", "featured_title_line2": "درمان تخصصی",
"featured_desc": "محصولات دارویی کانینا با استفاده از دانش پیشرفته بیوتکنولوژی و مواد اولیه ارگانیک نایاب فرآوری شده‌اند.", "featured_desc": "محصولات دارویی کنینا با استفاده از دانش پیشرفته بیوتکنولوژی و مواد اولیه ارگانیک نایاب فرآوری شده‌اند.",
"featured_view_all": "مشاهده تمامی محصولات", "featured_view_all": "مشاهده تمامی محصولات",
"product_view_details": "مشاهده جزییات", "product_view_details": "مشاهده جزییات",
"featured_no_products_title": "هیچ محصولی یافت نشد", "featured_no_products_title": "هیچ محصولی یافت نشد",
@ -79,7 +79,7 @@ const UI_TEXTS: Record<string, string> = {
"videos_badge": "Vet Insights", "videos_badge": "Vet Insights",
"videos_title": "مشاوره ویدئویی", "videos_title": "مشاوره ویدئویی",
"videos_title_highlight": "دوست دامپزشک پت شما", "videos_title_highlight": "دوست دامپزشک پت شما",
"videos_desc": "متخصصین آلمانی کانینا مستقیماً با شما صحبت می‌کنند. نحوه مصرف، مزایای رقابتی و دانش تخصصی پشت هر محصول.", "videos_desc": "متخصصین آلمانی کنینا مستقیماً با شما صحبت می‌کنند. نحوه مصرف، مزایای رقابتی و دانش تخصصی پشت هر محصول.",
"videos_view_all": "مشاهده همه ویدئوها", "videos_view_all": "مشاهده همه ویدئوها",
// === Navigation Cards (Home Page) === // === Navigation Cards (Home Page) ===
@ -87,7 +87,7 @@ const UI_TEXTS: Record<string, string> = {
"nav_card_shop_desc": "دسترسی به تمامی محصولات بر اساس علائم بالینی و نیازهای پت شما.", "nav_card_shop_desc": "دسترسی به تمامی محصولات بر اساس علائم بالینی و نیازهای پت شما.",
"nav_card_shop_cta": "کاتالوگ کامل محصولات", "nav_card_shop_cta": "کاتالوگ کامل محصولات",
"nav_card_wiki_title": "دانشنامه علمی", "nav_card_wiki_title": "دانشنامه علمی",
"nav_card_wiki_desc": "آشنایی با ترکیبات نایاب و استانداردهای دارویی محصولات آلمانی کانینا.", "nav_card_wiki_desc": "آشنایی با ترکیبات نایاب و استانداردهای دارویی محصولات آلمانی کنینا.",
"nav_card_wiki_cta": "مطالعه مقالات تخصصی", "nav_card_wiki_cta": "مطالعه مقالات تخصصی",
// === About Section === // === About Section ===
@ -102,12 +102,12 @@ const UI_TEXTS: Record<string, string> = {
// === CTA Section === // === CTA Section ===
"cta_title": "می‌خواهید بدانید کدام محصول برای پت شما مناسب‌تر است؟", "cta_title": "می‌خواهید بدانید کدام محصول برای پت شما مناسب‌تر است؟",
"cta_desc": "تیم متخصص دامپزشکی کانینا ایران آماده پاسخگویی به سوالات شماست.", "cta_desc": "تیم متخصص دامپزشکی کنینا ایران آماده پاسخگویی به سوالات شماست.",
"cta_btn_primary": "دریافت رژیم مکمل رایگان", "cta_btn_primary": "دریافت رژیم مکمل رایگان",
"cta_btn_secondary": "پروانه دامپزشکی و اصالت کالا", "cta_btn_secondary": "پروانه دامپزشکی و اصالت کالا",
// === Footer === // === Footer ===
"footer_brand_title": "کانینا ایران", "footer_brand_title": "کنینا ایران",
"footer_brand_subtitle": "نماینده رسمی در ایران", "footer_brand_subtitle": "نماینده رسمی در ایران",
"footer_brand_desc": "تدارک هوشمندانه سلامت برای همراهان وفادار شما. واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان با سابقه ۴۰ سال نوآوری.", "footer_brand_desc": "تدارک هوشمندانه سلامت برای همراهان وفادار شما. واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان با سابقه ۴۰ سال نوآوری.",
"footer_links_title": "دسترسی سریع", "footer_links_title": "دسترسی سریع",
@ -122,11 +122,11 @@ const UI_TEXTS: Record<string, string> = {
"footer_email_label": "مکاتبات رسمی", "footer_email_label": "مکاتبات رسمی",
"footer_email": "info@canino-iran.com", "footer_email": "info@canino-iran.com",
"footer_address_label": "دفتر مرکزی", "footer_address_label": "دفتر مرکزی",
"footer_address": "تهران، جردن، خیابان سعیدی، ساختمان کانینا، طبقه ۵، واحد ۱۹", "footer_address": "تهران، جردن، خیابان سعیدی، ساختمان کنینا، طبقه ۵، واحد ۱۹",
"footer_auth_title": "اصالت کالا تضمین می‌شود", "footer_auth_title": "اصالت کالا تضمین می‌شود",
"footer_auth_desc": "تمامی محصولات دارای لیبل اصالت وزارت بهداشت و QR Code اختصاصی هستند.", "footer_auth_desc": "تمامی محصولات دارای لیبل اصالت وزارت بهداشت و QR Code اختصاصی هستند.",
"footer_auth_badge": "اصالت آلمانی کانینا", "footer_auth_badge": "اصالت آلمانی کنینا",
"footer_copyright": "© ۲۰۲۶ کانینا ایران. تمامی حقوق محفوظ است.", "footer_copyright": "© ۲۰۲۶ کنینا ایران. تمامی حقوق محفوظ است.",
"footer_about": "درباره ما", "footer_about": "درباره ما",
"footer_contact": "تماس با ما", "footer_contact": "تماس با ما",
"footer_privacy": "حریم خصوصی", "footer_privacy": "حریم خصوصی",

View File

@ -53,7 +53,7 @@ async function main() {
const uiTexts = { const uiTexts = {
hero_badge: "از ۱۹۸۴ در خدمت سلامت حیوانات", hero_badge: "از ۱۹۸۴ در خدمت سلامت حیوانات",
hero_title: "تخصص آلمانی در خدمت\nسلامت پت‌های خانگی", hero_title: "تخصص آلمانی در خدمت\nسلامت پت‌های خانگی",
hero_desc: "از سال ۱۹۸۴، شرکت Canina pharma GmbH در برگیش گلادباخ آلمان، با بهره‌گیری از مواد اولیه طبیعی و فرآیندهای پیشرفته، استاندارد طلایی مکمل‌های دامپزشکی را تعریف می‌کند. کانینا نماینده رسمی این برند در ایران است.", hero_desc: "از سال ۱۹۸۴، شرکت Canina pharma GmbH در برگیش گلادباخ آلمان، با بهره‌گیری از مواد اولیه طبیعی و فرآیندهای پیشرفته، استاندارد طلایی مکمل‌های دامپزشکی را تعریف می‌کند. کنینا نماینده رسمی این برند در ایران است.",
hero_btn_advisor: "دستیار سلامت پت", hero_btn_advisor: "دستیار سلامت پت",
hero_btn_products: "مشاهده محصولات", hero_btn_products: "مشاهده محصولات",
hero_stat_founded: "۱۹۸۴ سال تأسیس", hero_stat_founded: "۱۹۸۴ سال تأسیس",
@ -63,12 +63,12 @@ async function main() {
hero_image_title: "مکمل‌های تایید شده دامپزشکی با گواهی IFS", hero_image_title: "مکمل‌های تایید شده دامپزشکی با گواهی IFS",
hero_quality_standard: "استاندارد کیفی آلمان (IFS & HACCP)", hero_quality_standard: "استاندارد کیفی آلمان (IFS & HACCP)",
cta_title: "می‌خواهید بدانید کدام محصول برای پت شما مناسب‌تر است؟", cta_title: "می‌خواهید بدانید کدام محصول برای پت شما مناسب‌تر است؟",
cta_subtitle: "تیم متخصص دامپزشکی کانینا ایران آماده پاسخگویی به سوالات شماست. با بیش از ۴۰ محصول تخصصی، راهکار مناسب هر نیاز بالینی را خواهید یافت.", cta_subtitle: "تیم متخصص دامپزشکی کنینا ایران آماده پاسخگویی به سوالات شماست. با بیش از ۴۰ محصول تخصصی، راهکار مناسب هر نیاز بالینی را خواهید یافت.",
cta_btn_free: "دریافت رژیم مکمل رایگان", cta_btn_free: "دریافت رژیم مکمل رایگان",
cta_btn_products: "ورود به محصولات تخصصی", cta_btn_products: "ورود به محصولات تخصصی",
about_teaser_badge: "داستان ما از آلمان", about_teaser_badge: "داستان ما از آلمان",
about_teaser_title: "Canina pharma GmbH – میراثی از ۱۹۸۴", about_teaser_title: "Canina pharma GmbH – میراثی از ۱۹۸۴",
about_teaser_desc: "شرکت Canina pharma GmbH در سال ۱۹۸۴ توسط پتر و کریستین کرشن‌مایر در شهر برگیش گلادباخ آلمان تأسیس شد. این شرکت با شعار «مادر طبیعت بهترین نسخه را می‌داند» از مواد اولیه طبیعی و تجدیدپذیر برای تولید مکمل‌های با گرید دارویی استفاده می‌کند. امروزه محصولات کانینا در بیش از ۴۰ کشور جهان توزیع می‌شود و نماینده رسمی آن در ایران با نام کانینا به ارائه محصولات اصل با پروانه دامپزشکی می‌پردازد.", about_teaser_desc: "شرکت Canina pharma GmbH در سال ۱۹۸۴ توسط پتر و کریستین کرشن‌مایر در شهر برگیش گلادباخ آلمان تأسیس شد. این شرکت با شعار «مادر طبیعت بهترین نسخه را می‌داند» از مواد اولیه طبیعی و تجدیدپذیر برای تولید مکمل‌های با گرید دارویی استفاده می‌کند. امروزه محصولات کنینا در بیش از ۴۰ کشور جهان توزیع می‌شود و نماینده رسمی آن در ایران با نام کنینا به ارائه محصولات اصل با پروانه دامپزشکی می‌پردازد.",
about_teaser_feat1_title: "مواد اولیه نایاب", about_teaser_feat1_title: "مواد اولیه نایاب",
about_teaser_feat1_desc: "استفاده از پودر صدف لب‌سبز اصل نیوزیلند (Perna canaliculus)، پیت طبیعی آلمان، آغوز و روغن‌های گیاهی ارگانیک با گرید دارویی.", about_teaser_feat1_desc: "استفاده از پودر صدف لب‌سبز اصل نیوزیلند (Perna canaliculus)، پیت طبیعی آلمان، آغوز و روغن‌های گیاهی ارگانیک با گرید دارویی.",
about_teaser_feat2_title: "فاقد مواد نگهدارنده", about_teaser_feat2_title: "فاقد مواد نگهدارنده",

View File

@ -339,7 +339,7 @@ export class SmsService {
{ {
id: config.otpBodyId, id: config.otpBodyId,
title: 'کد تایید ورود و ثبت‌نام (OTP)', title: 'کد تایید ورود و ثبت‌نام (OTP)',
body: 'کد ورود به کانینا: {0}', body: 'کد ورود به کنینا: {0}',
assigned: 'otp', assigned: 'otp',
}, },
{ {

View File

@ -100,7 +100,7 @@ export class ContactService {
{ {
key: 'address_info', key: 'address_info',
title: 'نشانی دفتر مرکزی', title: 'نشانی دفتر مرکزی',
value: 'تهران، جردن، خیابان سعیدی، ساختمان کانینا، طبقه ۵، واحد ۱۹', value: 'تهران، جردن، خیابان سعیدی، ساختمان کنینا، طبقه ۵، واحد ۱۹',
icon: 'map-pin', icon: 'map-pin',
order: 3, order: 3,
}, },

View File

@ -65,7 +65,7 @@ export class PaymentService {
callbackUrl, callbackUrl,
orderId: order.id, orderId: order.id,
mobile: order.user?.mobile || undefined, mobile: order.user?.mobile || undefined,
description: `پرداخت سفارش ${order.trackingNumber || ''} - پت‌شاپ کانینا`, description: `پرداخت سفارش ${order.trackingNumber || ''} - پت‌شاپ کنینا`,
}; };
// Create a pending PaymentTransaction in database // Create a pending PaymentTransaction in database
@ -159,7 +159,7 @@ export class PaymentService {
amountRials, amountRials,
callbackUrl, callbackUrl,
mobile: user.mobile || undefined, mobile: user.mobile || undefined,
description: `شارژ کیف پول - پت شاپ کانینا`, description: `شارژ کیف پول - پت شاپ کنینا`,
}; };
const transaction = await this.prisma.paymentTransaction.create({ const transaction = await this.prisma.paymentTransaction.create({

View File

@ -136,7 +136,7 @@ export class ZibalService implements IPaymentGateway {
merchant, merchant,
amount: params.amountRials, amount: params.amountRials,
callbackUrl: params.callbackUrl, callbackUrl: params.callbackUrl,
description: params.description || 'پرداخت سفارش در پت شاپ کانینا', description: params.description || 'پرداخت سفارش در پت شاپ کنینا',
orderId: params.orderId, orderId: params.orderId,
mobile: params.mobile, mobile: params.mobile,
nationalCode: params.nationalCode, nationalCode: params.nationalCode,

View File

@ -36,7 +36,7 @@ export class PrescriptionsService {
mobile: cleanPhone, mobile: cleanPhone,
role: 'Customer', role: 'Customer',
firstName: data.petName ? `سرپرست ${data.petName}` : 'کاربر', firstName: data.petName ? `سرپرست ${data.petName}` : 'کاربر',
lastName: 'کانینا', lastName: 'کنینا',
}, },
}); });
} }

View File

@ -1,5 +1,5 @@
import { ApiPropertyOptional } from '@nestjs/swagger'; import { ApiPropertyOptional } from '@nestjs/swagger';
import { IsOptional, IsString, IsEmail } from 'class-validator'; import { IsOptional, IsString, IsEmail, MinLength } from 'class-validator';
export class UpdateProfileDto { export class UpdateProfileDto {
@ApiPropertyOptional({ description: 'نام کاربر' }) @ApiPropertyOptional({ description: 'نام کاربر' })
@ -21,4 +21,15 @@ export class UpdateProfileDto {
@IsOptional() @IsOptional()
@IsString() @IsString()
mobile?: string; mobile?: string;
@ApiPropertyOptional({ description: 'رمز عبور جدید' })
@IsOptional()
@IsString()
@MinLength(6, { message: 'رمز عبور باید حداقل ۶ کاراکتر باشد' })
password?: string;
@ApiPropertyOptional({ description: 'رمز عبور فعلی (در صورت وجود)' })
@IsOptional()
@IsString()
currentPassword?: string;
} }

View File

@ -1,6 +1,13 @@
import { Injectable } from '@nestjs/common'; import {
Injectable,
BadRequestException,
NotFoundException,
} from '@nestjs/common';
import { Prisma } from '@prisma/client'; import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service'; import { PrismaService } from '../prisma/prisma.service';
import * as bcrypt from 'bcryptjs';
import { normalizeMobile } from '../common/utils/phone.utils';
import { UpdateProfileDto } from './dto/update-profile.dto';
export interface UserAddressInput { export interface UserAddressInput {
title: string; title: string;
@ -49,7 +56,13 @@ export class UsersService {
}, },
}); });
if (dbUser) return dbUser; if (dbUser) {
const { password, ...safeUser } = dbUser;
return {
...safeUser,
hasPassword: !!password,
};
}
return { return {
id: '12345678-1234-1234-1234-123456789012', id: '12345678-1234-1234-1234-123456789012',
@ -64,12 +77,13 @@ export class UsersService {
orders: [], orders: [],
addresses: [], addresses: [],
walletTransactions: [], walletTransactions: [],
hasPassword: true,
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
}; };
} }
return this.prisma.user.findUnique({ const user = await this.prisma.user.findUnique({
where: { id }, where: { id },
include: { include: {
pets: { pets: {
@ -98,12 +112,70 @@ export class UsersService {
}, },
}, },
}); });
if (!user) return null;
const { password, ...safeUser } = user;
return {
...safeUser,
hasPassword: !!password,
};
} }
async update(id: string, data: Prisma.UserUpdateInput) { async update(id: string, dto: UpdateProfileDto) {
return this.prisma.user.update({ const user = await this.prisma.user.findUnique({ where: { id } });
if (!user) {
throw new NotFoundException('کاربر یافت نشد');
}
const updateData: Prisma.UserUpdateInput = {};
if (dto.firstName !== undefined) updateData.firstName = dto.firstName.trim();
if (dto.lastName !== undefined) updateData.lastName = dto.lastName.trim();
if (dto.mobile !== undefined) {
const normalizedMobile = normalizeMobile(dto.mobile);
if (normalizedMobile && normalizedMobile !== user.mobile) {
const existing = await this.prisma.user.findUnique({
where: { mobile: normalizedMobile },
});
if (existing && existing.id !== id) {
throw new BadRequestException('این شماره موبایل قبلاً توسط کاربر دیگری ثبت شده است');
}
updateData.mobile = normalizedMobile;
}
}
if (dto.email !== undefined) {
const normalizedEmail = dto.email ? dto.email.trim().toLowerCase() : null;
if (normalizedEmail && normalizedEmail !== user.email) {
const existing = await this.prisma.user.findUnique({
where: { email: normalizedEmail },
});
if (existing && existing.id !== id) {
throw new BadRequestException('این آدرس ایمیل قبلاً توسط کاربر دیگری ثبت شده است');
}
}
updateData.email = normalizedEmail;
}
if (dto.password && dto.password.trim().length >= 6) {
// If user already has a password set, require and verify currentPassword
if (user.password) {
if (!dto.currentPassword) {
throw new BadRequestException('برای تغییر رمز عبور، وارد کردن رمز عبور فعلی الزامی است');
}
const isCurrentMatch = await bcrypt.compare(dto.currentPassword.trim(), user.password);
if (!isCurrentMatch) {
throw new BadRequestException('رمز عبور فعلی اشتباه است');
}
}
updateData.password = await bcrypt.hash(dto.password.trim(), 10);
}
const updatedUser = await this.prisma.user.update({
where: { id }, where: { id },
data, data: updateData,
include: { include: {
pets: true, pets: true,
orders: { orders: {
@ -117,6 +189,12 @@ export class UsersService {
walletTransactions: true, walletTransactions: true,
}, },
}); });
const { password, ...safeUser } = updatedUser;
return {
...safeUser,
hasPassword: !!password,
};
} }
async addAddress(userId: string, data: UserAddressInput) { async addAddress(userId: string, data: UserAddressInput) {

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
# معرفی پروژه کانینا ایران (Canina Iran) # معرفی پروژه کنینا ایران (Canina Iran)
## این پروژه چیست؟ ## این پروژه چیست؟
سامانه «کانینا ایران» پلتفرم تخصصی معرفی، فروش و مدیریت مکمل‌های درمانی حیوانات خانگی با گرید دارویی است. این سامانه به عنوان درگاه آنلاین نماینده انحصاری کانینا آلمان در ایران فعالیت می‌کند. هدف اصلی پروژه، ارائه یک تجربه کاربری (UX) بسیار غنی، سریع و در کلاس جهانی برای خریداران، کلینیک‌های دامپزشکی (مشتریان B2B) و همچنین فراهم‌کردن ابزارهای هوشمند (مانند مشاوره هوشمند و دانشنامه ترکیبات دارویی) است. سامانه «کنینا ایران» پلتفرم تخصصی معرفی، فروش و مدیریت مکمل‌های درمانی حیوانات خانگی با گرید دارویی است. این سامانه به عنوان درگاه آنلاین نماینده انحصاری کنینا آلمان در ایران فعالیت می‌کند. هدف اصلی پروژه، ارائه یک تجربه کاربری (UX) بسیار غنی، سریع و در کلاس جهانی برای خریداران، کلینیک‌های دامپزشکی (مشتریان B2B) و همچنین فراهم‌کردن ابزارهای هوشمند (مانند مشاوره هوشمند و دانشنامه ترکیبات دارویی) است.
## قابلیت‌های کلیدی سامانه ## قابلیت‌های کلیدی سامانه
سامانه به گونه‌ای طراحی شده تا تمام نیازهای یک کسب‌وکار مدرن e-commerce را در کنار قابلیت‌های تخصصی حوزه سلامت حیوانات برآورده کند: سامانه به گونه‌ای طراحی شده تا تمام نیازهای یک کسب‌وکار مدرن e-commerce را در کنار قابلیت‌های تخصصی حوزه سلامت حیوانات برآورده کند:

View File

@ -1,6 +1,6 @@
# راهنمای کاربری سیستم (User Guide) # راهنمای کاربری سیستم (User Guide)
سیستم کانینا از نظر نوع مخاطب به دو دسته کلی B2C (مشتریان عادی) و B2B (کلینیک‌ها و فروشگاه‌ها) تقسیم می‌شود. علاوه بر این، نقش «ادمین» برای مدیریت سیستم وجود دارد. سیستم کنینا از نظر نوع مخاطب به دو دسته کلی B2C (مشتریان عادی) و B2B (کلینیک‌ها و فروشگاه‌ها) تقسیم می‌شود. علاوه بر این، نقش «ادمین» برای مدیریت سیستم وجود دارد.
## ۱. مشتریان عادی (B2C Users) ## ۱. مشتریان عادی (B2C Users)
مشتری عادی کسی است که وارد سایت می‌شود تا برای حیوان خانگی خود مکمل خریداری کند یا اطلاعات کسب کند. مشتری عادی کسی است که وارد سایت می‌شود تا برای حیوان خانگی خود مکمل خریداری کند یا اطلاعات کسب کند.

View File

@ -1,6 +1,6 @@
# مستندات توسعه‌دهندگان (Developer Documentation) # مستندات توسعه‌دهندگان (Developer Documentation)
به مستندات رسمی سامانه «کانینا ایران» خوش آمدید. این مستندات به منظور راهنمایی توسعه‌دهندگان، مهندسین دواپس و تیم فنی شرکت نوشته شده است و نباید در دسترس کاربران عمومی قرار گیرد. به مستندات رسمی سامانه «کنینا ایران» خوش آمدید. این مستندات به منظور راهنمایی توسعه‌دهندگان، مهندسین دواپس و تیم فنی شرکت نوشته شده است و نباید در دسترس کاربران عمومی قرار گیرد.
در این دایرکتوری تمامی ساختارها، معماری‌ها، قوانین کدنویسی و مراحل انتشار (Deployment) به دقت تدوین شده‌اند تا سرعت 온‌بوردینگ (Onboarding) توسعه‌دهندگان جدید افزایش یافته و نگهداری کدهای قدیمی با کمترین ریسک انجام شود. در این دایرکتوری تمامی ساختارها، معماری‌ها، قوانین کدنویسی و مراحل انتشار (Deployment) به دقت تدوین شده‌اند تا سرعت 온‌بوردینگ (Onboarding) توسعه‌دهندگان جدید افزایش یافته و نگهداری کدهای قدیمی با کمترین ریسک انجام شود.

View File

@ -143,7 +143,7 @@ This document defines the strict integration contract between the NestJS backend
"authorName": "دکتر علیرضا محمدی", "authorName": "دکتر علیرضا محمدی",
"roleTitle": "دامپزشک و متخصص جراحی", "roleTitle": "دامپزشک و متخصص جراحی",
"avatarUrl": "https://example.com/avatar.jpg", "avatarUrl": "https://example.com/avatar.jpg",
"content": "فرمولاسیون کنهیدروکس کانینا در بهبودی پاهای O شکل سگ‌های نژاد بزرگ معجزه می‌کند.", "content": "فرمولاسیون کنهیدروکس کنینا در بهبودی پاهای O شکل سگ‌های نژاد بزرگ معجزه می‌کند.",
"rating": 5, "rating": 5,
"isFeatured": true, "isFeatured": true,
"isActive": true, "isActive": true,
@ -233,9 +233,9 @@ This document defines the strict integration contract between the NestJS backend
- **Response (200 OK)**: - **Response (200 OK)**:
```json ```json
{ {
"defaultMetaTitle": "کانینا ایران | مکمل‌های دارویی آلمانی حیوانات خانگی", "defaultMetaTitle": "کنینا ایران | مکمل‌های دارویی آلمانی حیوانات خانگی",
"defaultMetaDescription": "نمایندگی رسمی محصولات کانینا فارما آلمان", "defaultMetaDescription": "نمایندگی رسمی محصولات کنینا فارما آلمان",
"keywords": "مکمل سگ, مکمل گربه, کنهیدروکس, کانینا", "keywords": "مکمل سگ, مکمل گربه, کنهیدروکس, کنینا",
"canonicalBaseUrl": "https://caninairan.com", "canonicalBaseUrl": "https://caninairan.com",
"ogImageUrl": "https://caninairan.com/og-default.jpg" "ogImageUrl": "https://caninairan.com/og-default.jpg"
} }

View File

@ -98,7 +98,7 @@ export default function Sidebar({ isOpen, setIsOpen }: SidebarProps) {
<aside className={`w-64 bg-white border-l border-gray-200 h-screen fixed lg:fixed lg:right-0 lg:top-0 lg:bottom-0 flex flex-col font-vazir shadow-sm z-50 transform transition-transform duration-300 ease-in-out ${isOpen ? 'translate-x-0' : 'translate-x-full lg:translate-x-0' <aside className={`w-64 bg-white border-l border-gray-200 h-screen fixed lg:fixed lg:right-0 lg:top-0 lg:bottom-0 flex flex-col font-vazir shadow-sm z-50 transform transition-transform duration-300 ease-in-out ${isOpen ? 'translate-x-0' : 'translate-x-full lg:translate-x-0'
}`}> }`}>
<div className="h-16 flex items-center justify-center border-b border-gray-200 shrink-0"> <div className="h-16 flex items-center justify-center border-b border-gray-200 shrink-0">
<h1 className="text-xl font-black text-purple-600">کانینا | ادمین‌پنل</h1> <h1 className="text-xl font-black text-purple-600">کنینا | ادمین‌پنل</h1>
</div> </div>
<nav className="flex-1 overflow-y-auto py-4 px-3 space-y-6"> <nav className="flex-1 overflow-y-auto py-4 px-3 space-y-6">

View File

@ -67,7 +67,7 @@ export default function Dashboard() {
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h2 className="text-2xl font-black text-gray-900">داشبورد</h2> <h2 className="text-2xl font-black text-gray-900">داشبورد</h2>
<p className="text-gray-500 font-medium mt-1">خلاصه‌ای از وضعیت فروشگاه کانینا</p> <p className="text-gray-500 font-medium mt-1">خلاصه‌ای از وضعیت فروشگاه کنینا</p>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">

View File

@ -162,7 +162,7 @@ export default function IngredientsManager() {
<FlaskConical className="w-6 h-6 text-purple-600" /> <FlaskConical className="w-6 h-6 text-purple-600" />
مدیریت ترکیبات و دانشنامه علمی (Scientific Wiki) مدیریت ترکیبات و دانشنامه علمی (Scientific Wiki)
</h2> </h2>
<p className="text-gray-500 font-medium mt-1">مدیریت مواد موثره، ترکیبات دارویی و خواص درمانی محصولات کانینا</p> <p className="text-gray-500 font-medium mt-1">مدیریت مواد موثره، ترکیبات دارویی و خواص درمانی محصولات کنینا</p>
</div> </div>
<button <button
onClick={() => openModal()} onClick={() => openModal()}

View File

@ -94,7 +94,7 @@ export default function Login() {
ورود به پنل مدیریت ورود به پنل مدیریت
</h2> </h2>
<p className="mt-2 text-center text-sm text-gray-600 font-bold"> <p className="mt-2 text-center text-sm text-gray-600 font-bold">
کانینا | لابراتوار تخصصی مکمل‌های درمانی حیوانات کنینا | لابراتوار تخصصی مکمل‌های درمانی حیوانات
</p> </p>
</div> </div>

View File

@ -478,7 +478,7 @@ export default function MediaManager() {
type="text" type="text"
value={seoFormData.altText} value={seoFormData.altText}
onChange={(e) => setSeoFormData({ ...seoFormData, altText: e.target.value })} onChange={(e) => setSeoFormData({ ...seoFormData, altText: e.target.value })}
placeholder="مثال: عکس مکمل کانیدروکس گپ کانینا" placeholder="مثال: عکس مکمل کانیدروکس گپ کنینا"
className="w-full px-3 py-2 border rounded-xl text-sm" className="w-full px-3 py-2 border rounded-xl text-sm"
/> />
</div> </div>

View File

@ -186,7 +186,7 @@ export default function Orders() {
const paymentMethodText = orderToPrint.paymentMethod === 'wallet' ? 'پرداخت از کیف پول الکترونیک' : 'پرداخت آنلاین از درگاه شتاب'; const paymentMethodText = orderToPrint.paymentMethod === 'wallet' ? 'پرداخت از کیف پول الکترونیک' : 'پرداخت آنلاین از درگاه شتاب';
const itemsHtml = items.map((item: OrderItem) => { const itemsHtml = items.map((item: OrderItem) => {
const pName = item.product?.nameFa || item.product?.name || item.name || 'محصول کانینا'; const pName = item.product?.nameFa || item.product?.name || item.name || 'محصول کنینا';
const pImg = item.product?.imageUrl || item.product?.image || '/products/product-placeholder.png'; const pImg = item.product?.imageUrl || item.product?.image || '/products/product-placeholder.png';
const pPrice = Number(item.product?.priceValue || item.priceValue || 0); const pPrice = Number(item.product?.priceValue || item.priceValue || 0);
const rowTotal = pPrice * (item.quantity || 1); const rowTotal = pPrice * (item.quantity || 1);
@ -210,7 +210,7 @@ export default function Orders() {
<html dir="rtl" lang="fa"> <html dir="rtl" lang="fa">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>فاکتور رسمی کانینا - ${trackingCode}</title> <title>فاکتور رسمی کنینا - ${trackingCode}</title>
<style> <style>
@font-face { @font-face {
font-family: 'Vazirmatn'; font-family: 'Vazirmatn';
@ -238,7 +238,7 @@ export default function Orders() {
<div class="invoice-card"> <div class="invoice-card">
<div class="header"> <div class="header">
<div> <div>
<div class="logo-text">CANINA | کانینا</div> <div class="logo-text">CANINA | کنینا</div>
<div class="sub-logo">پنل مدیریت سفارشات آلمان</div> <div class="sub-logo">پنل مدیریت سفارشات آلمان</div>
</div> </div>
<div style="text-align: left;"> <div style="text-align: left;">
@ -303,7 +303,7 @@ export default function Orders() {
<div class="footer"> <div class="footer">
این فاکتور رسمی به صورت سیستمی صادر شده و فاقد نیاز به مهر و امضای فیزیکی است.<br/> این فاکتور رسمی به صورت سیستمی صادر شده و فاقد نیاز به مهر و امضای فیزیکی است.<br/>
پشتیبانی کانینا آلمان: ۰۲۱-۸۸۸۸۸۸۸۸ | www.canina.ir پشتیبانی کنینا آلمان: ۰۲۱-۸۸۸۸۸۸۸۸ | www.canina.ir
</div> </div>
</div> </div>
@ -581,7 +581,7 @@ export default function Orders() {
</h4> </h4>
<div className="space-y-3"> <div className="space-y-3">
{(selectedOrder.orderItems || selectedOrder.items || []).map((item: OrderItem, idx: number) => { {(selectedOrder.orderItems || selectedOrder.items || []).map((item: OrderItem, idx: number) => {
const pName = item.product?.nameFa || item.product?.name || item.name || 'محصول کانینا'; const pName = item.product?.nameFa || item.product?.name || item.name || 'محصول کنینا';
const pImg = item.product?.imageUrl || item.product?.image || '/products/product-placeholder.png'; const pImg = item.product?.imageUrl || item.product?.image || '/products/product-placeholder.png';
const pPrice = Number(item.product?.priceValue || item.priceValue || 0); const pPrice = Number(item.product?.priceValue || item.priceValue || 0);
const qty = item.quantity || 1; const qty = item.quantity || 1;

View File

@ -241,8 +241,8 @@ export default function Products() {
buyPrice: formData.buyPrice ? Number(formData.buyPrice) : 0, buyPrice: formData.buyPrice ? Number(formData.buyPrice) : 0,
wholesalePrice: formData.wholesalePrice ? Number(formData.wholesalePrice) : null, wholesalePrice: formData.wholesalePrice ? Number(formData.wholesalePrice) : null,
slug: derivedSlug, slug: derivedSlug,
metaTitle: formData.metaTitle.trim() || `${formData.nameFa || 'نام محصول'} | خرید و قیمت مکمل کانینا`, metaTitle: formData.metaTitle.trim() || `${formData.nameFa || 'نام محصول'} | خرید و قیمت مکمل کنینا`,
metaDescription: formData.metaDescription.trim() || formData.shortDescription || 'خرید آنلاین مکمل اصل کانینا آلمان با بالاترین کیفیت بالینی...', metaDescription: formData.metaDescription.trim() || formData.shortDescription || 'خرید آنلاین مکمل اصل کنینا آلمان با بالاترین کیفیت بالینی...',
canonicalUrl: formData.canonicalUrl.trim() || `/shop/${derivedSlug}` canonicalUrl: formData.canonicalUrl.trim() || `/shop/${derivedSlug}`
}; };
@ -918,8 +918,8 @@ export default function Products() {
<div className={activeTab === 'seo' ? 'block' : 'hidden'}> <div className={activeTab === 'seo' ? 'block' : 'hidden'}>
{(() => { {(() => {
const derivedSlug = formData.slug || slugify(formData.nameEn); const derivedSlug = formData.slug || slugify(formData.nameEn);
const derivedMetaTitle = formData.metaTitle || `${formData.nameFa || 'نام محصول'} | خرید و قیمت مکمل کانینا`; const derivedMetaTitle = formData.metaTitle || `${formData.nameFa || 'نام محصول'} | خرید و قیمت مکمل کنینا`;
const derivedMetaDescription = formData.metaDescription || (formData.shortDescription || 'خرید آنلاین مکمل اصل کانینا آلمان با بالاترین کیفیت بالینی و ضمانت اصالت فیزیکی...'); const derivedMetaDescription = formData.metaDescription || (formData.shortDescription || 'خرید آنلاین مکمل اصل کنینا آلمان با بالاترین کیفیت بالینی و ضمانت اصالت فیزیکی...');
const derivedCanonicalUrl = formData.canonicalUrl || `/shop/${derivedSlug}`; const derivedCanonicalUrl = formData.canonicalUrl || `/shop/${derivedSlug}`;
return ( return (

View File

@ -101,7 +101,7 @@ export default function Reports() {
onClick={() => { onClick={() => {
if (!reportData) return; if (!reportData) return;
const rows = [ const rows = [
['گزارش کلی سیستم کانینا ایران'], ['گزارش کلی سیستم کنینا ایران'],
['درآمد کل (تومان)', overview?.totalRevenue || 0], ['درآمد کل (تومان)', overview?.totalRevenue || 0],
['تعداد سفارشات', overview?.totalOrders || 0], ['تعداد سفارشات', overview?.totalOrders || 0],
['تعداد کاربران', overview?.totalUsers || 0], ['تعداد کاربران', overview?.totalUsers || 0],

View File

@ -8,9 +8,9 @@ import type { SeoSettings } from '../types/admin';
export default function SeoSettingsPage() { export default function SeoSettingsPage() {
const [seo, setSeo] = useState<SeoSettings>({ const [seo, setSeo] = useState<SeoSettings>({
defaultMetaTitle: 'کانینا ایران | مکمل‌های دارویی آلمانی حیوانات خانگی', defaultMetaTitle: 'کنینا ایران | مکمل‌های دارویی آلمانی حیوانات خانگی',
defaultMetaDescription: 'نمایندگی رسمی محصولات کانینا فارما آلمان در ایران', defaultMetaDescription: 'نمایندگی رسمی محصولات کنینا فارما آلمان در ایران',
keywords: 'مکمل سگ, مکمل گربه, کنهیدروکس, کانینا', keywords: 'مکمل سگ, مکمل گربه, کنهیدروکس, کنینا',
canonicalBaseUrl: 'https://caninairan.com', canonicalBaseUrl: 'https://caninairan.com',
ogImageUrl: 'https://caninairan.com/og-default.jpg', ogImageUrl: 'https://caninairan.com/og-default.jpg',
}); });

View File

@ -29,7 +29,7 @@ export default function Settings() {
SOCIAL_WHATSAPP: '09120000000', SOCIAL_WHATSAPP: '09120000000',
SOCIAL_INSTAGRAM: 'https://instagram.com/canina_iran', SOCIAL_INSTAGRAM: 'https://instagram.com/canina_iran',
SOCIAL_TELEGRAM: 'https://t.me/canina_iran', SOCIAL_TELEGRAM: 'https://t.me/canina_iran',
CONTACT_ADDRESS: 'تهران، جردن، خیابان سعیدی، ساختمان کانینا، طبقه ۵، واحد ۱۹', CONTACT_ADDRESS: 'تهران، جردن، خیابان سعیدی، ساختمان کنینا، طبقه ۵، واحد ۱۹',
BRAND_LOGO_URL: '/logo.png', BRAND_LOGO_URL: '/logo.png',
ENAMAD_CODE: '', ENAMAD_CODE: '',
THEME_PRIMARY_COLOR: '#7c3aed', THEME_PRIMARY_COLOR: '#7c3aed',
@ -66,7 +66,7 @@ export default function Settings() {
SOCIAL_WHATSAPP: response.data.data.SOCIAL_WHATSAPP || '09120000000', SOCIAL_WHATSAPP: response.data.data.SOCIAL_WHATSAPP || '09120000000',
SOCIAL_INSTAGRAM: response.data.data.SOCIAL_INSTAGRAM || 'https://instagram.com/canina_iran', SOCIAL_INSTAGRAM: response.data.data.SOCIAL_INSTAGRAM || 'https://instagram.com/canina_iran',
SOCIAL_TELEGRAM: response.data.data.SOCIAL_TELEGRAM || 'https://t.me/canina_iran', SOCIAL_TELEGRAM: response.data.data.SOCIAL_TELEGRAM || 'https://t.me/canina_iran',
CONTACT_ADDRESS: response.data.data.CONTACT_ADDRESS || 'تهران، جردن، خیابان سعیدی، ساختمان کانینا، طبقه ۵، واحد ۱۹', CONTACT_ADDRESS: response.data.data.CONTACT_ADDRESS || 'تهران، جردن، خیابان سعیدی، ساختمان کنینا، طبقه ۵، واحد ۱۹',
BRAND_LOGO_URL: response.data.data.BRAND_LOGO_URL || '/logo.png', BRAND_LOGO_URL: response.data.data.BRAND_LOGO_URL || '/logo.png',
ENAMAD_CODE: response.data.data.ENAMAD_CODE || '', ENAMAD_CODE: response.data.data.ENAMAD_CODE || '',
THEME_PRIMARY_COLOR: response.data.data.THEME_PRIMARY_COLOR || '#7c3aed', THEME_PRIMARY_COLOR: response.data.data.THEME_PRIMARY_COLOR || '#7c3aed',
@ -105,7 +105,7 @@ export default function Settings() {
<SettingsIcon className="w-6 h-6 text-purple-600" /> <SettingsIcon className="w-6 h-6 text-purple-600" />
تنظیمات فروشگاه تنظیمات فروشگاه
</h2> </h2>
<p className="text-gray-500 font-medium mt-1">مدیریت پارامترهای اصلی و سیستمی کانینا</p> <p className="text-gray-500 font-medium mt-1">مدیریت پارامترهای اصلی و سیستمی کنینا</p>
</div> </div>
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-2 flex-wrap">
<Link <Link
@ -212,7 +212,7 @@ export default function Settings() {
<div className="flex items-center justify-between p-4 bg-gray-50 rounded-xl border border-gray-200"> <div className="flex items-center justify-between p-4 bg-gray-50 rounded-xl border border-gray-200">
<div> <div>
<h4 className="font-bold text-gray-900 text-sm">کارت به کارت (واریز مستقیم)</h4> <h4 className="font-bold text-gray-900 text-sm">کارت به کارت (واریز مستقیم)</h4>
<p className="text-xs text-gray-500 mt-0.5">واریز به شماره کارت/شبا رسمی کانینا</p> <p className="text-xs text-gray-500 mt-0.5">واریز به شماره کارت/شبا رسمی کنینا</p>
</div> </div>
<label className="relative inline-flex items-center cursor-pointer"> <label className="relative inline-flex items-center cursor-pointer">
<input <input

View File

@ -1344,7 +1344,7 @@ export default function SmsSettingsPage() {
required required
value={newPatternTitle} value={newPatternTitle}
onChange={(e) => setNewPatternTitle(e.target.value)} onChange={(e) => setNewPatternTitle(e.target.value)}
placeholder="مثلاً: تایید سفارش کانینا" placeholder="مثلاً: تایید سفارش کنینا"
className="w-full border border-gray-200 rounded-xl p-3 outline-none focus:ring-2 focus:ring-purple-500 text-sm font-bold" className="w-full border border-gray-200 rounded-xl p-3 outline-none focus:ring-2 focus:ring-purple-500 text-sm font-bold"
/> />
</div> </div>
@ -1372,7 +1372,7 @@ export default function SmsSettingsPage() {
required required
value={newPatternBody} value={newPatternBody}
onChange={(e) => setNewPatternBody(e.target.value)} onChange={(e) => setNewPatternBody(e.target.value)}
placeholder="کاربر گرامی، سفارش {0} با مبلغ {1} تومان در کانینا ثبت شد." placeholder="کاربر گرامی، سفارش {0} با مبلغ {1} تومان در کنینا ثبت شد."
className="w-full border border-gray-200 rounded-xl p-3 outline-none focus:ring-2 focus:ring-purple-500 text-sm font-medium leading-relaxed" className="w-full border border-gray-200 rounded-xl p-3 outline-none focus:ring-2 focus:ring-purple-500 text-sm font-medium leading-relaxed"
/> />
</div> </div>

View File

@ -182,7 +182,7 @@ export default function UITexts() {
مدیریت محتوا و بنرهای صفحات (Page Builder) مدیریت محتوا و بنرهای صفحات (Page Builder)
</h1> </h1>
<p className="text-sm text-gray-500 font-bold mt-1"> <p className="text-sm text-gray-500 font-bold mt-1">
ویرایش زنده متون، تصاویر بنر، لینک دکمه‌ها و عناوین تمام صفحات سایت کانینا ویرایش زنده متون، تصاویر بنر، لینک دکمه‌ها و عناوین تمام صفحات سایت کنینا
</p> </p>
</div> </div>
</div> </div>

View File

@ -173,7 +173,7 @@ export default function Videos() {
مدیریت ویدئوها و آکادمی آموزشی مدیریت ویدئوها و آکادمی آموزشی
</h1> </h1>
<p className="text-sm text-gray-500 font-bold mt-1"> <p className="text-sm text-gray-500 font-bold mt-1">
آپلود مستقیم ویدئو/کاور یا درج کد آیفرم آپارات/یوتیوب برای مشاوره ویدئویی دامپزشکان کانینا آپلود مستقیم ویدئو/کاور یا درج کد آیفرم آپارات/یوتیوب برای مشاوره ویدئویی دامپزشکان کنینا
</p> </p>
</div> </div>
<button <button

View File

@ -134,7 +134,7 @@ export default function Wiki() {
<div> <div>
<h2 className="text-2xl font-black text-gray-900 flex items-center gap-2"> <h2 className="text-2xl font-black text-gray-900 flex items-center gap-2">
<BookOpen className="w-6 h-6 text-purple-600" /> <BookOpen className="w-6 h-6 text-purple-600" />
دانشنامه کانینا (Wiki) دانشنامه کنینا (Wiki)
</h2> </h2>
<p className="text-gray-500 font-medium mt-1">مدیریت اصطلاحات علمی، مواد تشکیل‌دهنده و خواص درمانی</p> <p className="text-gray-500 font-medium mt-1">مدیریت اصطلاحات علمی، مواد تشکیل‌دهنده و خواص درمانی</p>
</div> </div>

View File

@ -5,7 +5,7 @@ This version has breaking changes — APIs, conventions, and file structure may
<!-- END:nextjs-agent-rules --> <!-- END:nextjs-agent-rules -->
# Brand Name # Brand Name
نام برند رسمی «**کانینا**» / «**Canina**» است (نه «کانینو» / «Canino»). در سرتاسر پروژه باید از Canina / کانینا استفاده شود. نام برند رسمی «**کنینا**» / «**Canina**» است (نه «کانینو» / «Canino»). در سرتاسر پروژه باید از Canina / کنینا استفاده شود.
- CSS class names: `canina-blue`, `canina-gold`, `canina-teal`, `canina-dark` - CSS class names: `canina-blue`, `canina-gold`, `canina-teal`, `canina-dark`
- Store/cookie names: `canina-user`, `canina-cart`, `canina-pets` - Store/cookie names: `canina-user`, `canina-cart`, `canina-pets`
- URL `https://canino-iran.com` (domain name, بدون تغییر می‌ماند) - URL `https://canino-iran.com` (domain name, بدون تغییر می‌ماند)

View File

@ -92,7 +92,7 @@ export default function HomeClient({ initialData }: HomeClientProps) {
{getText('nav_card_wiki_title', "دانشنامه علمی")} {getText('nav_card_wiki_title', "دانشنامه علمی")}
</h3> </h3>
<p className="text-medical-gray-600 mb-8 leading-relaxed font-shabnam"> <p className="text-medical-gray-600 mb-8 leading-relaxed font-shabnam">
{getText('nav_card_wiki_desc', "آشنایی با ترکیبات نایاب و استانداردهای دارویی محصولات آلمانی کانینا.")} {getText('nav_card_wiki_desc', "آشنایی با ترکیبات نایاب و استانداردهای دارویی محصولات آلمانی کنینا.")}
</p> </p>
<div className="flex items-center gap-2 text-emerald-700 font-black tracking-widest uppercase text-xs font-vazir"> <div className="flex items-center gap-2 text-emerald-700 font-black tracking-widest uppercase text-xs font-vazir">
{getText('nav_card_wiki_cta', "مطالعه مقالات تخصصی")} {getText('nav_card_wiki_cta', "مطالعه مقالات تخصصی")}
@ -109,7 +109,7 @@ export default function HomeClient({ initialData }: HomeClientProps) {
<div className="absolute -top-10 -right-10 w-40 h-40 bg-canina-blue/5 rounded-full blur-3xl" /> <div className="absolute -top-10 -right-10 w-40 h-40 bg-canina-blue/5 rounded-full blur-3xl" />
<Image <Image
src="/assets/images/regenerated_image_1779109861747.png" src="/assets/images/regenerated_image_1779109861747.png"
alt="آزمایشگاه تخصصی تولید مکمل‌های دارویی کانینا آلمان" alt="آزمایشگاه تخصصی تولید مکمل‌های دارویی کنینا آلمان"
className="rounded-[3rem] shadow-2xl relative z-10 grayscale-[10%] w-full" className="rounded-[3rem] shadow-2xl relative z-10 grayscale-[10%] w-full"
width={640} width={640}
height={425} height={425}
@ -172,7 +172,7 @@ export default function HomeClient({ initialData }: HomeClientProps) {
{getText('cta_title', "می‌خواهید بدانید کدام محصول برای پت شما مناسب‌تر است؟")} {getText('cta_title', "می‌خواهید بدانید کدام محصول برای پت شما مناسب‌تر است؟")}
</h2> </h2>
<p className="text-white/80 text-lg mb-10 leading-relaxed font-shabnam"> <p className="text-white/80 text-lg mb-10 leading-relaxed font-shabnam">
{getText('cta_desc', "تیم متخصص دامپزشکی کانینا ایران آماده پاسخگویی به سوالات شماست.")} {getText('cta_desc', "تیم متخصص دامپزشکی کنینا ایران آماده پاسخگویی به سوالات شماست.")}
</p> </p>
<div className="flex flex-wrap justify-center gap-4"> <div className="flex flex-wrap justify-center gap-4">
<button className="bg-white text-canina-blue px-12 py-5 rounded-full font-black text-xl hover:scale-105 transition-transform shadow-2xl shadow-black/20 font-shabnam"> <button className="bg-white text-canina-blue px-12 py-5 rounded-full font-black text-xl hover:scale-105 transition-transform shadow-2xl shadow-black/20 font-shabnam">

View File

@ -3,15 +3,15 @@ import { ShieldCheck, History, Award, CheckCircle } from "lucide-react";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "درباره ما - میراث ۴۰ ساله کانیتا آلمان در ایران", title: "درباره ما - میراث ۴۰ ساله کانیتا آلمان در ایران",
description: "داستان کانینا ایران و میراث ۴۰ ساله شرکت Canina pharma GmbH آلمان در تولید مکمل‌های فوق‌تخصصی دامپزشکی با گرید دارویی اختصاصی.", description: "داستان کنینا ایران و میراث ۴۰ ساله شرکت Canina pharma GmbH آلمان در تولید مکمل‌های فوق‌تخصصی دامپزشکی با گرید دارویی اختصاصی.",
alternates: { alternates: {
canonical: "/about", canonical: "/about",
}, },
openGraph: { openGraph: {
title: "درباره ما - کانینا ایران", title: "درباره ما - کنینا ایران",
description: "داستان کانینا ایران و میراث ۴۰ ساله شرکت Canina pharma GmbH آلمان در تولید مکمل‌های فوق‌تخصصی دامپزشکی.", description: "داستان کنینا ایران و میراث ۴۰ ساله شرکت Canina pharma GmbH آلمان در تولید مکمل‌های فوق‌تخصصی دامپزشکی.",
url: "https://canina-iran.com/about", url: "https://canina-iran.com/about",
siteName: "کانینا ایران", siteName: "کنینا ایران",
locale: "fa_IR", locale: "fa_IR",
type: "website", type: "website",
} }
@ -29,7 +29,7 @@ export default function AboutPage() {
داستان ما از ۱۹۸۴ داستان ما از ۱۹۸۴
</span> </span>
<h1 className="text-4xl lg:text-5xl font-black text-medical-gray-900 leading-tight"> <h1 className="text-4xl lg:text-5xl font-black text-medical-gray-900 leading-tight">
درباره <span className="text-canina-blue italic">کانینا ایران</span> درباره <span className="text-canina-blue italic">کنینا ایران</span>
</h1> </h1>
<p className="text-lg text-medical-gray-500 max-w-2xl mx-auto leading-relaxed"> <p className="text-lg text-medical-gray-500 max-w-2xl mx-auto leading-relaxed">
میراث علمی و تعهد ما به تدارک هوشمندانه سلامت برای همراهان وفادار شما. میراث علمی و تعهد ما به تدارک هوشمندانه سلامت برای همراهان وفادار شما.
@ -50,7 +50,7 @@ export default function AboutPage() {
{/* Narrative Section */} {/* Narrative Section */}
<div className="space-y-8 text-medical-gray-700 leading-relaxed"> <div className="space-y-8 text-medical-gray-700 leading-relaxed">
<p> <p>
امروز محصولات کانینا در بیش از ۴۰ کشور جهان توزیع می‌شود. کانینا ایران به عنوان نماینده رسمی این برند، با هدف ارتقای کیفیت زندگی حیوانات خانگی و دسترسی آسان جامعه دامپزشکی و دارندگان حیوانات به این مکمل‌های ارزشمند، فعالیت خود را آغاز نموده است. تمامی محصولات با بسته‌بندی اصلی آلمان و دارای برچسب اصالت سازمان دامپزشکی ارائه می‌شوند. امروز محصولات کنینا در بیش از ۴۰ کشور جهان توزیع می‌شود. کنینا ایران به عنوان نماینده رسمی این برند، با هدف ارتقای کیفیت زندگی حیوانات خانگی و دسترسی آسان جامعه دامپزشکی و دارندگان حیوانات به این مکمل‌های ارزشمند، فعالیت خود را آغاز نموده است. تمامی محصولات با بسته‌بندی اصلی آلمان و دارای برچسب اصالت سازمان دامپزشکی ارائه می‌شوند.
</p> </p>
</div> </div>

View File

@ -18,7 +18,7 @@ async function getBlog(slug: string) {
excerpt: b.metaDescription || b.content.substring(0, 150).replace(/<[^>]+>/g, '') + '...', excerpt: b.metaDescription || b.content.substring(0, 150).replace(/<[^>]+>/g, '') + '...',
image: b.imageUrl, image: b.imageUrl,
date: new Date(b.createdAt).toLocaleDateString('fa-IR'), date: new Date(b.createdAt).toLocaleDateString('fa-IR'),
author: b.author ? `${b.author.firstName} ${b.author.lastName}` : 'نویسنده کانینا', author: b.author ? `${b.author.firstName} ${b.author.lastName}` : 'نویسنده کنینا',
content: b.content content: b.content
}; };
} catch (error) { } catch (error) {
@ -32,9 +32,9 @@ export const revalidate = 60; // ensure SSR dynamic revalidation works correctly
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> { export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params; const { slug } = await params;
const blog = await getBlog(slug); const blog = await getBlog(slug);
if (!blog) return { title: 'مقاله یافت نشد | کانینا ایران' }; if (!blog) return { title: 'مقاله یافت نشد | کنینا ایران' };
const title = `${blog.title} | مجله سلامت کانینا`; const title = `${blog.title} | مجله سلامت کنینا`;
const description = blog.excerpt.substring(0, 150).trim(); const description = blog.excerpt.substring(0, 150).trim();
return { return {
@ -48,7 +48,7 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
description, description,
images: [blog.image], images: [blog.image],
url: `/blog/${slug}`, url: `/blog/${slug}`,
siteName: "کانینا ایران", siteName: "کنینا ایران",
type: "article", type: "article",
} }
}; };
@ -112,7 +112,7 @@ export default async function BlogPostPage({ params }: { params: Promise<{ slug:
</div> </div>
<div> <div>
<div className="font-black text-medical-gray-900 text-lg">{selectedPost.author}</div> <div className="font-black text-medical-gray-900 text-lg">{selectedPost.author}</div>
<div className="font-bold text-medical-gray-400">نویسنده کانینا</div> <div className="font-bold text-medical-gray-400">نویسنده کنینا</div>
</div> </div>
</div> </div>
<div className="flex gap-4"> <div className="flex gap-4">

View File

@ -2,16 +2,16 @@ import BlogPage from "../../components/BlogPage";
import type { Metadata } from 'next'; import type { Metadata } from 'next';
export const metadata: Metadata = { export const metadata: Metadata = {
title: "مجله سلامت حیوانات خانگی و مقالات علمی - کانینا ایران", title: "مجله سلامت حیوانات خانگی و مقالات علمی - کنینا ایران",
description: "آخرین مقالات، آموزش‌های دامپزشکی و اخبار علمی روز دنیا در زمینه نگهداری، تغذیه تخصصی و سلامت سگ و گربه.", description: "آخرین مقالات، آموزش‌های دامپزشکی و اخبار علمی روز دنیا در زمینه نگهداری، تغذیه تخصصی و سلامت سگ و گربه.",
alternates: { alternates: {
canonical: "/blog", canonical: "/blog",
}, },
openGraph: { openGraph: {
title: "مجله سلامت حیوانات خانگی - کانینا ایران", title: "مجله سلامت حیوانات خانگی - کنینا ایران",
description: "آخرین مقالات، آموزش‌ها و اخبار علمی در زمینه نگهداری، تغذیه و سلامت سگ و گربه.", description: "آخرین مقالات، آموزش‌ها و اخبار علمی در زمینه نگهداری، تغذیه و سلامت سگ و گربه.",
url: "https://canina-iran.com/blog", url: "https://canina-iran.com/blog",
siteName: "کانینا ایران", siteName: "کنینا ایران",
locale: "fa_IR", locale: "fa_IR",
type: "website", type: "website",
} }

View File

@ -3,15 +3,15 @@ import CatalogClient from "./CatalogClient";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "کاتالوگ محصولات Canina - راهنمای مکمل‌های درمانی سگ و گربه", title: "کاتالوگ محصولات Canina - راهنمای مکمل‌های درمانی سگ و گربه",
description: "کاتالوگ جامع محصولات آلمانی کانینا شامل راهنمای ترکیبات، گرید دارویی، دوز مصرفی و موارد درمان بالینی مفصل، پوست و مو و رشد پت‌ها.", description: "کاتالوگ جامع محصولات آلمانی کنینا شامل راهنمای ترکیبات، گرید دارویی، دوز مصرفی و موارد درمان بالینی مفصل، پوست و مو و رشد پت‌ها.",
alternates: { alternates: {
canonical: "/catalog", canonical: "/catalog",
}, },
openGraph: { openGraph: {
title: "کاتالوگ رسمی مکمل‌های درمانی Canina Pharma Germany", title: "کاتالوگ رسمی مکمل‌های درمانی Canina Pharma Germany",
description: "دانلود و مشاهده آنلاین کاتالوگ مکمل‌های دارویی حیوانات خانگی کانینا آلمان", description: "دانلود و مشاهده آنلاین کاتالوگ مکمل‌های دارویی حیوانات خانگی کنینا آلمان",
url: "https://canina-iran.com/catalog", url: "https://canina-iran.com/catalog",
siteName: "کانینا ایران", siteName: "کنینا ایران",
locale: "fa_IR", locale: "fa_IR",
type: "website", type: "website",
} }

View File

@ -2,16 +2,16 @@ import type { Metadata } from "next";
import ContactFormClient from "@/components/ContactFormClient"; import ContactFormClient from "@/components/ContactFormClient";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "تماس با ما - نمایندگی رسمی مکمل‌های کانینا آلمان در ایران", title: "تماس با ما - نمایندگی رسمی مکمل‌های کنینا آلمان در ایران",
description: "راه‌های ارتباطی، تلفن تماس ویژه، آدرس دفتر مرکزی و فرم تماس مستقیم با نمایندگی انحصاری کانیتا آلمان در ایران.", description: "راه‌های ارتباطی، تلفن تماس ویژه، آدرس دفتر مرکزی و فرم تماس مستقیم با نمایندگی انحصاری کانیتا آلمان در ایران.",
alternates: { alternates: {
canonical: "/contact", canonical: "/contact",
}, },
openGraph: { openGraph: {
title: "تماس با ما - کانینا ایران", title: "تماس با ما - کنینا ایران",
description: "راه‌های ارتباطی با نمایندگی رسمی مکمل‌های دامپزشکی کانینا در ایران.", description: "راه‌های ارتباطی با نمایندگی رسمی مکمل‌های دامپزشکی کنینا در ایران.",
url: "https://canina-iran.com/contact", url: "https://canina-iran.com/contact",
siteName: "کانینا ایران", siteName: "کنینا ایران",
locale: "fa_IR", locale: "fa_IR",
type: "website", type: "website",
} }
@ -28,7 +28,7 @@ export default function ContactPage() {
پاسخگوی شما هستیم پاسخگوی شما هستیم
</span> </span>
<h1 className="text-4xl lg:text-5xl font-black text-medical-gray-900 leading-tight"> <h1 className="text-4xl lg:text-5xl font-black text-medical-gray-900 leading-tight">
تماس با <span className="text-canina-blue italic">کانینا ایران</span> تماس با <span className="text-canina-blue italic">کنینا ایران</span>
</h1> </h1>
<p className="text-lg text-medical-gray-500 max-w-2xl mx-auto leading-relaxed"> <p className="text-lg text-medical-gray-500 max-w-2xl mx-auto leading-relaxed">
جهت مشاوره تخصصی، خرید عمده و پیگیری سفارشات با ما در ارتباط باشید. جهت مشاوره تخصصی، خرید عمده و پیگیری سفارشات با ما در ارتباط باشید.

View File

@ -22,10 +22,10 @@ export async function generateMetadata(): Promise<Metadata> {
} }
const siteUrl = seoSettings?.canonicalBaseUrl || process.env.NEXT_PUBLIC_SITE_URL || 'https://canina-iran.com'; const siteUrl = seoSettings?.canonicalBaseUrl || process.env.NEXT_PUBLIC_SITE_URL || 'https://canina-iran.com';
const titleDefault = seoSettings?.defaultMetaTitle || 'کانینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی'; const titleDefault = seoSettings?.defaultMetaTitle || 'کنینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی';
const descriptionDefault = seoSettings?.defaultMetaDescription || 'تدارک هوشمندانه سلامت برای همراهان وفادار شما. کانینا ایران واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان.'; const descriptionDefault = seoSettings?.defaultMetaDescription || 'تدارک هوشمندانه سلامت برای همراهان وفادار شما. کنینا ایران واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان.';
let keywordsList: string[] = ['مکمل حیوانات خانگی', 'مکمل سگ', 'مکمل گربه', 'کانینا', 'داروی حیوانات', 'Canina Iran']; let keywordsList: string[] = ['مکمل حیوانات خانگی', 'مکمل سگ', 'مکمل گربه', 'کنینا', 'داروی حیوانات', 'Canina Iran'];
if (seoSettings?.keywords) { if (seoSettings?.keywords) {
if (Array.isArray(seoSettings.keywords)) { if (Array.isArray(seoSettings.keywords)) {
keywordsList = seoSettings.keywords; keywordsList = seoSettings.keywords;
@ -39,7 +39,7 @@ export async function generateMetadata(): Promise<Metadata> {
return { return {
metadataBase: new URL(siteUrl), metadataBase: new URL(siteUrl),
title: { title: {
template: '%s | کانینا ایران', template: '%s | کنینا ایران',
default: titleDefault, default: titleDefault,
}, },
description: descriptionDefault, description: descriptionDefault,
@ -59,7 +59,7 @@ export async function generateMetadata(): Promise<Metadata> {
title: titleDefault, title: titleDefault,
description: descriptionDefault, description: descriptionDefault,
url: siteUrl, url: siteUrl,
siteName: 'کانینا ایران', siteName: 'کنینا ایران',
locale: 'fa_IR', locale: 'fa_IR',
type: 'website', type: 'website',
images: [ images: [
@ -99,7 +99,7 @@ export default function RootLayout({
const jsonLd = { const jsonLd = {
'@context': 'https://schema.org', '@context': 'https://schema.org',
'@type': 'Organization', '@type': 'Organization',
name: 'کانینا ایران', name: 'کنینا ایران',
url: 'https://canina-iran.com', url: 'https://canina-iran.com',
logo: 'https://canina.de/media/fb/d5/47/1683116813/Logo.png', logo: 'https://canina.de/media/fb/d5/47/1683116813/Logo.png',
contactPoint: { contactPoint: {

View File

@ -2,16 +2,16 @@ import type { Metadata } from "next";
import HomeClient from "./HomeClient"; import HomeClient from "./HomeClient";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "کانینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی", title: "کنینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی",
description: "تدارک هوشمندانه سلامت برای همراهان وفادار شما. کانینا ایران واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان با پروانه رسمی دامپزشکی.", description: "تدارک هوشمندانه سلامت برای همراهان وفادار شما. کنینا ایران واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان با پروانه رسمی دامپزشکی.",
alternates: { alternates: {
canonical: "/", canonical: "/",
}, },
openGraph: { openGraph: {
title: "کانینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی", title: "کنینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی",
description: "واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان", description: "واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان",
url: "https://canina-iran.com", url: "https://canina-iran.com",
siteName: "کانینا ایران", siteName: "کنینا ایران",
locale: "fa_IR", locale: "fa_IR",
type: "website", type: "website",
images: [ images: [
@ -19,13 +19,13 @@ export const metadata: Metadata = {
url: "/assets/images/regenerated_image_1779109861747.png", url: "/assets/images/regenerated_image_1779109861747.png",
width: 1200, width: 1200,
height: 630, height: 630,
alt: "کانینا ایران - مکمل‌های درمانی حیوانات خانگی", alt: "کنینا ایران - مکمل‌های درمانی حیوانات خانگی",
} }
] ]
}, },
twitter: { twitter: {
card: "summary_large_image", card: "summary_large_image",
title: "کانینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی", title: "کنینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی",
description: "تدارک هوشمندانه سلامت برای همراهان وفادار شما.", description: "تدارک هوشمندانه سلامت برای همراهان وفادار شما.",
images: ["/assets/images/regenerated_image_1779109861747.png"], images: ["/assets/images/regenerated_image_1779109861747.png"],
} }

View File

@ -211,7 +211,7 @@ function VerifyContent() {
{orderDetails?.trackingNumber && ( {orderDetails?.trackingNumber && (
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-medical-gray-500 font-bold">کد پیگیری سفارش کانینا:</span> <span className="text-medical-gray-500 font-bold">کد پیگیری سفارش کنینا:</span>
<span className="font-mono font-black text-canina-blue text-sm dir-ltr"> <span className="font-mono font-black text-canina-blue text-sm dir-ltr">
{orderDetails.trackingNumber} {orderDetails.trackingNumber}
</span> </span>

View File

@ -2,16 +2,16 @@ import type { Metadata } from "next";
import { Shield } from "lucide-react"; import { Shield } from "lucide-react";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "حریم خصوصی کاربران - کانینا ایران", title: "حریم خصوصی کاربران - کنینا ایران",
description: "سیاست‌های حفاظت از اطلاعات شخصی کاربران، امنیت داده‌های پزشکی و پرونده‌های درمانی پت در وب‌سایت کانینا ایران.", description: "سیاست‌های حفاظت از اطلاعات شخصی کاربران، امنیت داده‌های پزشکی و پرونده‌های درمانی پت در وب‌سایت کنینا ایران.",
alternates: { alternates: {
canonical: "/privacy", canonical: "/privacy",
}, },
openGraph: { openGraph: {
title: "حریم خصوصی کاربران - کانینا ایران", title: "حریم خصوصی کاربران - کنینا ایران",
description: "سیاست‌های حفاظت از اطلاعات شخصی کاربران در وب‌سایت کانینا ایران.", description: "سیاست‌های حفاظت از اطلاعات شخصی کاربران در وب‌سایت کنینا ایران.",
url: "https://canina-iran.com/privacy", url: "https://canina-iran.com/privacy",
siteName: "کانینا ایران", siteName: "کنینا ایران",
locale: "fa_IR", locale: "fa_IR",
type: "website", type: "website",
} }
@ -31,7 +31,7 @@ export default function PrivacyPage() {
حریم خصوصی کاربران حریم خصوصی کاربران
</h1> </h1>
<p className="text-lg text-medical-gray-500 max-w-2xl mx-auto leading-relaxed"> <p className="text-lg text-medical-gray-500 max-w-2xl mx-auto leading-relaxed">
تعهد ما به امنیت داده‌ها و اطلاعات شخصی ثبت شده در وب‌سایت کانینا ایران. تعهد ما به امنیت داده‌ها و اطلاعات شخصی ثبت شده در وب‌سایت کنینا ایران.
</p> </p>
</div> </div>
@ -54,7 +54,7 @@ export default function PrivacyPage() {
<section className="space-y-4"> <section className="space-y-4">
<h3 className="text-xl font-black text-medical-gray-900">۳. امنیت داده‌ها</h3> <h3 className="text-xl font-black text-medical-gray-900">۳. امنیت داده‌ها</h3>
<p> <p>
تمامی اطلاعات حساس کاربران با استفاده از پروتکل‌های امنیتی استاندارد و بستر امن HTTPS رمزگذاری شده و بر روی سرورهای ابری محافظت شده ذخیره می‌گردند. تیم فنی کانینا متعهد به پایش مداوم و به‌روزرسانی سیستم‌های امنیتی جهت جلوگیری از دسترسی‌های غیرمجاز است. تمامی اطلاعات حساس کاربران با استفاده از پروتکل‌های امنیتی استاندارد و بستر امن HTTPS رمزگذاری شده و بر روی سرورهای ابری محافظت شده ذخیره می‌گردند. تیم فنی کنینا متعهد به پایش مداوم و به‌روزرسانی سیستم‌های امنیتی جهت جلوگیری از دسترسی‌های غیرمجاز است.
</p> </p>
</section> </section>

View File

@ -3,7 +3,7 @@ import type { Metadata } from 'next';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'جستجوی محصولات', title: 'جستجوی محصولات',
description: 'جستجوی مکمل‌های درمانی حیوانات خانگی، داروها و مقالات سلامت در کانینا ایران.', description: 'جستجوی مکمل‌های درمانی حیوانات خانگی، داروها و مقالات سلامت در کنینا ایران.',
}; };
export default async function SearchPage({ searchParams }: { searchParams: Promise<{ q?: string }> }) { export default async function SearchPage({ searchParams }: { searchParams: Promise<{ q?: string }> }) {

View File

@ -11,13 +11,13 @@ export async function generateMetadata(
if (!product) { if (!product) {
return { return {
title: 'خرید مکمل درمانی پت | کانینا ایران', title: 'خرید مکمل درمانی پت | کنینا ایران',
}; };
} }
const nameFa = product.nameFa || product.name; const nameFa = product.nameFa || product.name;
const nameEn = product.nameEn || ""; const nameEn = product.nameEn || "";
const title = nameFa.includes("Canina") ? nameFa : `${nameFa} (${nameEn}) | برند کانینا آلمان`; const title = nameFa.includes("Canina") ? nameFa : `${nameFa} (${nameEn}) | برند کنینا آلمان`;
const description = (product.shortDescription || product.description || '').substring(0, 150).trim(); const description = (product.shortDescription || product.description || '').substring(0, 150).trim();
const rawKeywords = (product as unknown as Record<string, unknown>).keywords; const rawKeywords = (product as unknown as Record<string, unknown>).keywords;
const keywords = typeof rawKeywords === 'string' ? rawKeywords.split(',') : product.main_ingredients; const keywords = typeof rawKeywords === 'string' ? rawKeywords.split(',') : product.main_ingredients;
@ -31,7 +31,7 @@ export async function generateMetadata(
description, description,
images: [product.image], images: [product.image],
url: `/shop/${resolvedParams.slug}`, url: `/shop/${resolvedParams.slug}`,
siteName: "کانینا ایران", siteName: "کنینا ایران",
type: "website", type: "website",
}, },
alternates: { alternates: {
@ -74,7 +74,7 @@ export default async function ShopProductPage({ params }: { params: Promise<{ sl
itemCondition: 'https://schema.org/NewCondition', itemCondition: 'https://schema.org/NewCondition',
seller: { seller: {
'@type': 'Organization', '@type': 'Organization',
name: 'کانینا ایران' name: 'کنینا ایران'
} }
} }
}; };

View File

@ -2,16 +2,16 @@ import type { Metadata } from "next";
import ArchivePage from "../../components/ArchivePage"; import ArchivePage from "../../components/ArchivePage";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "فروشگاه تخصصی مکمل‌های دارویی - کانینا ایران", title: "فروشگاه تخصصی مکمل‌های دارویی - کنینا ایران",
description: "خرید آنلاین انواع مکمل‌های درمانی، مفاصل و استخوان، گوارش و ایمنی، ویتامین‌ها و مراقبت‌های ویژه سگ و گربه با تضمین اصالت و گواهی سلامت دامپزشکی.", description: "خرید آنلاین انواع مکمل‌های درمانی، مفاصل و استخوان، گوارش و ایمنی، ویتامین‌ها و مراقبت‌های ویژه سگ و گربه با تضمین اصالت و گواهی سلامت دامپزشکی.",
alternates: { alternates: {
canonical: "/shop", canonical: "/shop",
}, },
openGraph: { openGraph: {
title: "فروشگاه تخصصی مکمل‌های دارویی - کانینا ایران", title: "فروشگاه تخصصی مکمل‌های دارویی - کنینا ایران",
description: "خرید آنلاین انواع مکمل‌های درمانی، مفاصل و استخوان، گوارش و ایمنی، ویتامین‌ها و مراقبت‌های ویژه سگ و گربه.", description: "خرید آنلاین انواع مکمل‌های درمانی، مفاصل و استخوان، گوارش و ایمنی، ویتامین‌ها و مراقبت‌های ویژه سگ و گربه.",
url: "https://canina-iran.com/shop", url: "https://canina-iran.com/shop",
siteName: "کانینا ایران", siteName: "کنینا ایران",
locale: "fa_IR", locale: "fa_IR",
type: "website", type: "website",
} }

View File

@ -4,8 +4,8 @@ import EnamadBadge from '@/components/EnamadBadge';
import { ShieldCheck, Award, FileCheck, CheckCircle2, Building2 } from 'lucide-react'; import { ShieldCheck, Award, FileCheck, CheckCircle2, Building2 } from 'lucide-react';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'نمادهای اعتماد و مجوزهای رسمی | کانینا ایران', title: 'نمادهای اعتماد و مجوزهای رسمی | کنینا ایران',
description: 'مشاهده نماد اعتماد الکترونیکی (اینماد)، مجوزهای سازمان دامپزشکی کشور و گواهینامه‌های اصالت کالاهای کانینا ایران.', description: 'مشاهده نماد اعتماد الکترونیکی (اینماد)، مجوزهای سازمان دامپزشکی کشور و گواهینامه‌های اصالت کالاهای کنینا ایران.',
}; };
export default function TrustSealsPage() { export default function TrustSealsPage() {
@ -19,7 +19,7 @@ export default function TrustSealsPage() {
color: 'from-teal-500 to-emerald-600', color: 'from-teal-500 to-emerald-600',
}, },
{ {
title: 'پروانه بهداشتی واردات محصولات کانینا', title: 'پروانه بهداشتی واردات محصولات کنینا',
authority: 'سازمان دامپزشکی کل کشور', authority: 'سازمان دامپزشکی کل کشور',
code: 'IVC-8840192', code: 'IVC-8840192',
status: 'تایید شده رسمی', status: 'تایید شده رسمی',
@ -56,7 +56,7 @@ export default function TrustSealsPage() {
تضمین اصالت و اطمینان خرید تضمین اصالت و اطمینان خرید
</div> </div>
<h1 className="text-3xl md:text-5xl font-extrabold tracking-tight mb-4 leading-tight"> <h1 className="text-3xl md:text-5xl font-extrabold tracking-tight mb-4 leading-tight">
نمادهای اعتماد و مجوزهای قانونی کانینا نمادهای اعتماد و مجوزهای قانونی کنینا
</h1> </h1>
<p className="text-teal-100 text-base md:text-lg leading-relaxed"> <p className="text-teal-100 text-base md:text-lg leading-relaxed">
تمام کلیه مکمل‌ها، ویتامین‌ها و داروها به صورت مستقیم از کمپانی Canina Pharma آلمان وارد شده و دارای کلیه مجوزهای رسمی سازمان دامپزشکی و نمادهای تجارت الکترونیک می‌باشند. تمام کلیه مکمل‌ها، ویتامین‌ها و داروها به صورت مستقیم از کمپانی Canina Pharma آلمان وارد شده و دارای کلیه مجوزهای رسمی سازمان دامپزشکی و نمادهای تجارت الکترونیک می‌باشند.
@ -101,13 +101,13 @@ export default function TrustSealsPage() {
<section className="bg-white rounded-2xl p-8 border border-gray-100 shadow-sm mb-12"> <section className="bg-white rounded-2xl p-8 border border-gray-100 shadow-sm mb-12">
<h2 className="text-xl font-extrabold text-gray-900 mb-6 flex items-center gap-2"> <h2 className="text-xl font-extrabold text-gray-900 mb-6 flex items-center gap-2">
<CheckCircle2 className="w-6 h-6 text-teal-600" /> <CheckCircle2 className="w-6 h-6 text-teal-600" />
تعهدات کانینا ایران به مشتریان تعهدات کنینا ایران به مشتریان
</h2> </h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="p-4 rounded-xl bg-teal-50/50 border border-teal-100"> <div className="p-4 rounded-xl bg-teal-50/50 border border-teal-100">
<h3 className="font-bold text-teal-900 mb-2">۱۰۰٪ گارانتی اصالت کالا</h3> <h3 className="font-bold text-teal-900 mb-2">۱۰۰٪ گارانتی اصالت کالا</h3>
<p className="text-xs text-teal-700 leading-relaxed"> <p className="text-xs text-teal-700 leading-relaxed">
تمامی مکمل‌ها دارای هولوگرام اصلی کمپانی کانینا آلمان و تاریخ انقضای معتبر و بروز می‌باشند. تمامی مکمل‌ها دارای هولوگرام اصلی کمپانی کنینا آلمان و تاریخ انقضای معتبر و بروز می‌باشند.
</p> </p>
</div> </div>
<div className="p-4 rounded-xl bg-emerald-50/50 border border-emerald-100"> <div className="p-4 rounded-xl bg-emerald-50/50 border border-emerald-100">
@ -119,7 +119,7 @@ export default function TrustSealsPage() {
<div className="p-4 rounded-xl bg-amber-50/50 border border-amber-100"> <div className="p-4 rounded-xl bg-amber-50/50 border border-amber-100">
<h3 className="font-bold text-amber-900 mb-2">پشتیبانی تخصصی دامپزشکی</h3> <h3 className="font-bold text-amber-900 mb-2">پشتیبانی تخصصی دامپزشکی</h3>
<p className="text-xs text-amber-700 leading-relaxed"> <p className="text-xs text-amber-700 leading-relaxed">
امکان مشاوره و بررسی دوز مصرفی مکمل‌ها زیر نظر دامپزشکان متخصص مجموعه کانینا. امکان مشاوره و بررسی دوز مصرفی مکمل‌ها زیر نظر دامپزشکان متخصص مجموعه کنینا.
</p> </p>
</div> </div>
</div> </div>

View File

@ -3,16 +3,16 @@ import IngredientWiki from "../../components/IngredientWiki";
import type { Metadata } from 'next'; import type { Metadata } from 'next';
export const metadata: Metadata = { export const metadata: Metadata = {
title: "دانشنامه ترکیبات دارویی و علمی - کانینا ایران", title: "دانشنامه ترکیبات دارویی و علمی - کنینا ایران",
description: "اطلاعات کامل پزشکی در مورد مواد موثره، ویتامین‌ها و ترکیبات گیاهی نایاب به کار رفته در محصولات تخصصی کانیتا آلمان.", description: "اطلاعات کامل پزشکی در مورد مواد موثره، ویتامین‌ها و ترکیبات گیاهی نایاب به کار رفته در محصولات تخصصی کانیتا آلمان.",
alternates: { alternates: {
canonical: "/wiki", canonical: "/wiki",
}, },
openGraph: { openGraph: {
title: "دانشنامه ترکیبات دارویی و علمی - کانینا ایران", title: "دانشنامه ترکیبات دارویی و علمی - کنینا ایران",
description: "اطلاعات کامل در مورد مواد موثره، ویتامین‌ها و ترکیبات گیاهی به کار رفته در محصولات تخصصی کانینا ایران.", description: "اطلاعات کامل در مورد مواد موثره، ویتامین‌ها و ترکیبات گیاهی به کار رفته در محصولات تخصصی کنینا ایران.",
url: "https://canina-iran.com/wiki", url: "https://canina-iran.com/wiki",
siteName: "کانینا ایران", siteName: "کنینا ایران",
locale: "fa_IR", locale: "fa_IR",
type: "website", type: "website",
} }

View File

@ -1,11 +1,25 @@
"use client"; "use client";
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef, useCallback } from "react";
import { motion, AnimatePresence } from "motion/react"; import { motion, AnimatePresence } from "motion/react";
import { X, Building2, ShieldCheck, Phone, Key, LogIn, ArrowRight, RefreshCw, Lock, Upload, CheckCircle2 } from "lucide-react"; import {
X,
Building2,
ShieldCheck,
Phone,
Key,
LogIn,
ArrowRight,
RefreshCw,
Lock,
Upload,
CheckCircle2,
Eye,
EyeOff,
} from "lucide-react";
import { useUserStore } from "../lib/store/userStore"; import { useUserStore } from "../lib/store/userStore";
import { authService } from "../lib/services/authService"; import { authService } from "../lib/services/authService";
import { toast } from "sonner"; import { toast } from "sonner";
import { toPersian } from "../lib/utils"; import { toPersian, toEnglishDigits } from "../lib/utils";
interface AuthModalProps { interface AuthModalProps {
isOpen: boolean; isOpen: boolean;
@ -14,7 +28,16 @@ interface AuthModalProps {
export default function AuthModal({ isOpen, onClose }: AuthModalProps) { export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const { fetchProfile } = useUserStore(); const { fetchProfile } = useUserStore();
const [view, setView] = useState<"login" | "register" | "otp-phone" | "otp" | "forgot-password" | "forgot-otp" | "reset-password" | "wholesale-request">("login"); const [view, setView] = useState<
| "login"
| "register"
| "otp-phone"
| "otp"
| "forgot-password"
| "forgot-otp"
| "reset-password"
| "wholesale-request"
>("login");
const [medicalLicense, setMedicalLicense] = useState(""); const [medicalLicense, setMedicalLicense] = useState("");
const [businessName, setBusinessName] = useState(""); const [businessName, setBusinessName] = useState("");
const [documentUploaded, setDocumentUploaded] = useState(false); const [documentUploaded, setDocumentUploaded] = useState(false);
@ -27,6 +50,8 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const [otpCode, setOtpCode] = useState(""); const [otpCode, setOtpCode] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [countdown, setCountdown] = useState(0); const [countdown, setCountdown] = useState(0);
const [showPassword, setShowPassword] = useState(false);
const [showNewPassword, setShowNewPassword] = useState(false);
const otpInputRef = useRef<HTMLInputElement>(null); const otpInputRef = useRef<HTMLInputElement>(null);
useEffect(() => { useEffect(() => {
@ -52,6 +77,8 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
setOtpCode(""); setOtpCode("");
setIsLoading(false); setIsLoading(false);
setCountdown(0); setCountdown(0);
setShowPassword(false);
setShowNewPassword(false);
}); });
} }
}, [isOpen]); }, [isOpen]);
@ -64,13 +91,72 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
} }
}, [countdown]); }, [countdown]);
const handleLogin = async (e: React.FormEvent) => { const triggerVerifyOtp = useCallback(
e.preventDefault(); async (codeToVerify: string) => {
if (!phoneNumber || !password) return toast.error("شماره موبایل و رمز عبور الزامی است"); const cleanCode = toEnglishDigits(codeToVerify).trim();
const cleanPhone = toEnglishDigits(phoneNumber).trim();
if (cleanCode.length !== 5 || !cleanPhone) return;
setIsLoading(true); setIsLoading(true);
try { try {
const res = await authService.login(phoneNumber, password); const response = await authService.verifyOtp(cleanPhone, cleanCode);
if (response.success) {
await fetchProfile();
toast.success("ورود با موفقیت انجام شد");
onClose();
}
} catch (err: unknown) {
const errObj = err as { message?: string };
toast.error(errObj.message || "کد تایید نامعتبر است");
} finally {
setIsLoading(false);
}
},
[phoneNumber, fetchProfile, onClose],
);
// WebOTP API SMS Auto-read
useEffect(() => {
if (view === "otp" && typeof window !== "undefined" && "OTPCredential" in window) {
const ac = new AbortController();
(navigator as unknown as { credentials: { get: (opts: unknown) => Promise<{ code?: string }> } }).credentials
?.get({
otp: { transport: ["sms"] },
signal: ac.signal,
})
.then((otp) => {
if (otp?.code) {
const clean = toEnglishDigits(otp.code).replace(/[^0-9]/g, "").slice(0, 5);
setOtpCode(clean);
if (clean.length === 5) {
triggerVerifyOtp(clean);
}
}
})
.catch(() => {});
return () => {
ac.abort();
};
}
}, [view, triggerVerifyOtp]);
const handleOtpChange = (val: string) => {
const clean = toEnglishDigits(val).replace(/[^0-9]/g, "").slice(0, 5);
setOtpCode(clean);
if (clean.length === 5) {
triggerVerifyOtp(clean);
}
};
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
const cleanPhone = toEnglishDigits(phoneNumber).trim();
if (!cleanPhone || !password) return toast.error("شماره موبایل و رمز عبور الزامی است");
setIsLoading(true);
try {
const res = await authService.login(cleanPhone, password.trim());
if (res.success) { if (res.success) {
await fetchProfile(); await fetchProfile();
toast.success("با موفقیت وارد شدید"); toast.success("با موفقیت وارد شدید");
@ -86,13 +172,20 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const handleRegister = async (e: React.FormEvent) => { const handleRegister = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
if (!firstName || !lastName || !phoneNumber || !password) { const cleanPhone = toEnglishDigits(phoneNumber).trim();
if (!firstName || !lastName || !cleanPhone || !password) {
return toast.error("لطفاً فیلدهای الزامی را پر کنید"); return toast.error("لطفاً فیلدهای الزامی را پر کنید");
} }
setIsLoading(true); setIsLoading(true);
try { try {
const res = await authService.register({ firstName, lastName, mobile: phoneNumber, email, password }); const res = await authService.register({
firstName: firstName.trim(),
lastName: lastName.trim(),
mobile: cleanPhone,
email: email ? email.trim() : undefined,
password: password.trim(),
});
if (res.success) { if (res.success) {
await fetchProfile(); await fetchProfile();
toast.success("ثبت‌نام با موفقیت انجام شد"); toast.success("ثبت‌نام با موفقیت انجام شد");
@ -108,7 +201,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const handleSendOtp = async (e: React.FormEvent) => { const handleSendOtp = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
const cleanPhone = phoneNumber.trim(); const cleanPhone = toEnglishDigits(phoneNumber).trim();
if (!/^09\d{9}$/.test(cleanPhone)) { if (!/^09\d{9}$/.test(cleanPhone)) {
toast.error("شماره موبایل نامعتبر است"); toast.error("شماره موبایل نامعتبر است");
return; return;
@ -135,33 +228,14 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const handleVerifyOtp = async (e: React.FormEvent) => { const handleVerifyOtp = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
const cleanCode = otpCode.trim(); triggerVerifyOtp(otpCode);
if (cleanCode.length !== 5) {
toast.error("کد باید ۵ رقم باشد");
return;
}
setIsLoading(true);
try {
const response = await authService.verifyOtp(phoneNumber.trim(), cleanCode);
if (response.success) {
await fetchProfile();
toast.success("ورود موفقیت‌آمیز بود");
onClose();
}
} catch (err: unknown) {
const errObj = err as { message?: string };
toast.error(errObj.message || "کد تایید نامعتبر است");
} finally {
setIsLoading(false);
}
}; };
const handleResendOtp = async () => { const handleResendOtp = async () => {
if (countdown > 0) return; if (countdown > 0) return;
setIsLoading(true); setIsLoading(true);
try { try {
await authService.sendOtp(phoneNumber.trim()); await authService.sendOtp(toEnglishDigits(phoneNumber).trim());
toast.success("کد تایید جدید ارسال شد"); toast.success("کد تایید جدید ارسال شد");
setCountdown(120); setCountdown(120);
} catch (err: unknown) { } catch (err: unknown) {
@ -174,7 +248,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const handleForgotSendOtp = async (e: React.FormEvent) => { const handleForgotSendOtp = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
const cleanPhone = phoneNumber.trim(); const cleanPhone = toEnglishDigits(phoneNumber).trim();
if (!/^09\d{9}$/.test(cleanPhone)) { if (!/^09\d{9}$/.test(cleanPhone)) {
toast.error("شماره موبایل نامعتبر است"); toast.error("شماره موبایل نامعتبر است");
return; return;
@ -200,14 +274,14 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const handleForgotVerifyOtp = async (e: React.FormEvent) => { const handleForgotVerifyOtp = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
if (otpCode.trim().length !== 5) { const cleanCode = toEnglishDigits(otpCode).trim();
if (cleanCode.length !== 5) {
toast.error("کد باید ۵ رقم باشد"); toast.error("کد باید ۵ رقم باشد");
return; return;
} }
setIsLoading(true); setIsLoading(true);
try { try {
// Verify OTP - if success, allow password reset await authService.verifyOtp(toEnglishDigits(phoneNumber).trim(), cleanCode);
await authService.verifyOtp(phoneNumber.trim(), otpCode.trim());
setView("reset-password"); setView("reset-password");
} catch (err: unknown) { } catch (err: unknown) {
const errObj = err as { message?: string }; const errObj = err as { message?: string };
@ -219,18 +293,18 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const handleResetPassword = async (e: React.FormEvent) => { const handleResetPassword = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
if (newPassword.length < 6) { if (!newPassword || newPassword.length < 6) {
toast.error("رمز جدید باید حداقل ۶ کاراکتر باشد"); return toast.error("رمز عبور جدید باید حداقل ۶ کاراکتر باشد");
return;
} }
setIsLoading(true); setIsLoading(true);
try { try {
await authService.updateProfile({ password: newPassword } as unknown as Parameters<typeof authService.updateProfile>[0]); await authService.updateProfile({ password: newPassword.trim() });
toast.success("رمز عبور با موفقیت تغییر یافت"); await fetchProfile();
setView("login"); toast.success("رمز عبور جدید با موفقیت تنظیم شد");
onClose();
} catch (err: unknown) { } catch (err: unknown) {
const errObj = err as { message?: string }; const errObj = err as { message?: string };
toast.error(errObj.message || "خطا در تغییر رمز"); toast.error(errObj.message || "خطا در تغییر رمز عبور");
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
@ -239,52 +313,36 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
return ( return (
<AnimatePresence> <AnimatePresence>
{isOpen && ( {isOpen && (
<> <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
{/* Backdrop */}
<motion.div <motion.div
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-[60]"
onClick={onClose} onClick={onClose}
className="absolute inset-0 bg-medical-gray-900/60 backdrop-blur-sm"
/> />
{/* Modal content */}
<motion.div <motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }} initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }} animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }} exit={{ opacity: 0, scale: 0.95, y: 20 }}
className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-md bg-white rounded-[2.5rem] shadow-2xl z-[70] overflow-hidden border border-medical-gray-100 font-vazir" className="relative w-full max-w-md bg-white rounded-[2.5rem] shadow-2xl overflow-hidden border border-medical-gray-100 z-10"
dir="rtl"
> >
{/* Top highlight bar */} {/* Header */}
<div className="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-canina-blue to-indigo-500" /> <div className="p-6 pb-0 flex justify-between items-center">
<div className="flex items-center gap-2 text-canina-blue font-black text-sm">
<div className="p-8 relative"> <ShieldCheck className="w-5 h-5" />
{/* Close Button */} <span>ورود امن به کانینو</span>
</div>
<button <button
onClick={onClose} onClick={onClose}
className="absolute top-6 left-6 p-2 rounded-full hover:bg-medical-gray-50 text-medical-gray-400 hover:text-medical-gray-600 transition-colors" className="w-10 h-10 rounded-full bg-medical-gray-50 flex items-center justify-center text-medical-gray-400 hover:bg-medical-gray-100 transition-colors"
> >
<X className="w-6 h-6" /> <X className="w-5 h-5" />
</button> </button>
{/* Title Header */}
<div className="flex flex-col items-center text-center mb-8 pt-4">
<div className="w-20 h-20 bg-canina-blue/10 rounded-3xl flex items-center justify-center mb-4 shadow-inner">
<ShieldCheck className="w-10 h-10 text-canina-blue" />
</div>
<h2 className="text-3xl font-black text-medical-gray-900 mb-2 italic">
ورود به دنیای کانینا
</h2>
<p className="text-medical-gray-500 text-sm font-bold max-w-[280px]">
{view === "otp"
? `کد تایید ارسال شده به شماره ${toPersian(phoneNumber)} را وارد کنید`
: "برای دسترسی به پرونده سلامت و سفارشات وارد شوید"}
</p>
</div> </div>
{/* Content Panel */} <div className="p-8">
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
{view === "login" && ( {view === "login" && (
<motion.form <motion.form
@ -295,58 +353,77 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
onSubmit={handleLogin} onSubmit={handleLogin}
className="space-y-4" className="space-y-4"
> >
<div className="space-y-2"> <div className="space-y-1">
<label htmlFor="login-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل</label> <div className="flex justify-between items-center">
<input <label className="text-xs font-bold text-medical-gray-500">شماره موبایل</label>
id="login-phone"
name="username"
type="tel"
maxLength={11}
autoComplete="username"
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-left tracking-widest"
/>
</div>
<div className="space-y-2">
<label htmlFor="login-password" className="text-xs font-bold text-medical-gray-500">رمز عبور</label>
<input
id="login-password"
name="password"
type="password"
autoComplete="current-password"
placeholder="رمز عبور خود را وارد کنید"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left"
/>
</div>
<div className="flex items-center justify-between pt-2 flex-wrap gap-2">
<button <button
type="button" type="button"
onClick={() => setView("otp-phone")} onClick={() => setView("otp-phone")}
className="text-xs font-bold text-canina-blue hover:underline" className="text-xs font-bold text-canina-blue hover:underline"
> >
ورود با کد یکبار مصرف (OTP) ورود با کد پیامکی (OTP)
</button> </button>
<div className="flex items-center gap-3"> </div>
<div className="relative">
<Phone className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-medical-gray-400" />
<input
autoFocus
type="tel"
maxLength={11}
required
placeholder="۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ""))}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 pr-11 pl-4 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono tracking-wider"
dir="ltr"
/>
</div>
</div>
<div className="space-y-1">
<div className="flex justify-between items-center">
<label className="text-xs font-bold text-medical-gray-500">رمز عبور</label>
<button <button
type="button" type="button"
onClick={() => { setPhoneNumber(""); setView("forgot-password"); }} onClick={() => setView("forgot-password")}
className="text-xs font-bold text-medical-gray-400 hover:text-canina-blue hover:underline" className="text-xs font-bold text-medical-gray-400 hover:text-canina-blue"
> >
فراموشی رمز فراموشی رمز عبور؟
</button> </button>
</div>
<div className="relative">
<Lock className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-medical-gray-400 pointer-events-none" />
<input
type={showPassword ? "text" : "password"}
autoComplete="current-password"
required
placeholder="رمز عبور..."
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 pr-11 pl-11 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono tracking-wider"
dir="ltr"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute left-3 top-1/2 -translate-y-1/2 p-1.5 text-medical-gray-400 hover:text-medical-gray-700 transition-colors cursor-pointer"
tabIndex={-1}
title={showPassword ? "مخفی کردن رمز" : "نمایش رمز"}
>
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
</div>
<div className="flex items-center justify-between text-xs pt-1">
<div className="flex items-center gap-1 text-medical-gray-400">
<span>حساب کاربری ندارید؟</span>
<button <button
type="button" type="button"
onClick={() => setView("register")} onClick={() => setView("register")}
className="text-xs font-bold text-canina-blue hover:underline" className="font-bold text-canina-blue hover:underline"
> >
حساب ندارید؟ ثبت‌نام ثبت‌نام عادی
</button> </button>
</div> </div>
</div> </div>
@ -355,10 +432,10 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
<button <button
type="button" type="button"
onClick={() => setView("wholesale-request")} onClick={() => setView("wholesale-request")}
className="w-full py-3 bg-amber-50 text-amber-900 border border-amber-200 rounded-xl text-xs font-black flex items-center justify-center gap-2 hover:bg-amber-100 transition-colors" className="w-full py-3 bg-amber-50 text-amber-900 border border-amber-200 rounded-xl text-xs font-black flex items-center justify-center gap-2 hover:bg-amber-100 transition-colors cursor-pointer"
> >
<Building2 className="w-4 h-4 text-amber-600" /> <Building2 className="w-4 h-4 text-amber-600" />
درخواست ثبت‌نام حساب خریدار عمده (پت‌شاپ / کلینیک) درخواست حساب خریدار عمده (پت‌شاپ / کلینیک)
</button> </button>
</div> </div>
@ -380,7 +457,9 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
exit={{ opacity: 0, x: -20 }} exit={{ opacity: 0, x: -20 }}
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
toast.success("درخواست احراز هویت خریدار عمده با موفقیت ارسال شد و پس از بررسی ادمین تایید خواهد شد."); toast.success(
"درخواست احراز هویت خریدار عمده با موفقیت ارسال شد و پس از بررسی ادمین تایید خواهد شد.",
);
setView("login"); setView("login");
}} }}
className="space-y-3" className="space-y-3"
@ -395,7 +474,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
type="text" type="text"
required required
value={businessName} value={businessName}
onChange={e => setBusinessName(e.target.value)} onChange={(e) => setBusinessName(e.target.value)}
placeholder="مثال: کلینیک دامپزشکی رازی" placeholder="مثال: کلینیک دامپزشکی رازی"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm"
/> />
@ -407,9 +486,10 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
type="text" type="text"
required required
value={medicalLicense} value={medicalLicense}
onChange={e => setMedicalLicense(e.target.value)} onChange={(e) => setMedicalLicense(e.target.value)}
placeholder="مثال: ۹۸۷۶۵۴۳۲۱" placeholder="مثال: ۹۸۷۶۵۴۳۲۱"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono"
dir="ltr"
/> />
</div> </div>
@ -420,9 +500,10 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
required required
maxLength={11} maxLength={11}
value={phoneNumber} value={phoneNumber}
onChange={e => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))} onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ""))}
placeholder="۰۹۱۲..." placeholder="۰۹۱۲..."
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono"
dir="ltr"
/> />
</div> </div>
@ -431,7 +512,9 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
setDocumentUploaded(true); setDocumentUploaded(true);
toast.success("تصویر پروانه با موفقیت بارگذاری شد."); toast.success("تصویر پروانه با موفقیت بارگذاری شد.");
}} }}
className={`p-4 border-2 border-dashed rounded-xl text-center cursor-pointer transition-all ${documentUploaded ? 'border-green-500 bg-green-50' : 'border-medical-gray-300 hover:border-canina-blue'}`} className={`p-4 border-2 border-dashed rounded-xl text-center cursor-pointer transition-all ${
documentUploaded ? "border-green-500 bg-green-50" : "border-medical-gray-300 hover:border-canina-blue"
}`}
> >
{documentUploaded ? ( {documentUploaded ? (
<div className="flex items-center justify-center gap-2 text-green-700 font-bold text-xs"> <div className="flex items-center justify-center gap-2 text-green-700 font-bold text-xs">
@ -450,7 +533,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
<button <button
type="button" type="button"
onClick={() => setView("login")} onClick={() => setView("login")}
className="text-xs font-bold text-canina-blue hover:underline" className="text-xs font-bold text-canina-blue hover:underline cursor-pointer"
> >
بازگشت به ورود بازگشت به ورود
</button> </button>
@ -480,12 +563,10 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
<label htmlFor="reg-fname" className="text-xs font-bold text-medical-gray-500">نام</label> <label htmlFor="reg-fname" className="text-xs font-bold text-medical-gray-500">نام</label>
<input <input
id="reg-fname" id="reg-fname"
name="given-name"
type="text" type="text"
autoComplete="given-name" required
value={firstName} value={firstName}
onChange={(e) => setFirstName(e.target.value)} onChange={(e) => setFirstName(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm"
/> />
</div> </div>
@ -493,29 +574,27 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
<label htmlFor="reg-lname" className="text-xs font-bold text-medical-gray-500">نام خانوادگی</label> <label htmlFor="reg-lname" className="text-xs font-bold text-medical-gray-500">نام خانوادگی</label>
<input <input
id="reg-lname" id="reg-lname"
name="family-name"
type="text" type="text"
autoComplete="family-name" required
value={lastName} value={lastName}
onChange={(e) => setLastName(e.target.value)} onChange={(e) => setLastName(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm"
/> />
</div> </div>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label htmlFor="reg-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل *</label> <label htmlFor="reg-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل</label>
<input <input
id="reg-phone" id="reg-phone"
name="username"
type="tel" type="tel"
required
maxLength={11} maxLength={11}
autoComplete="username" placeholder="۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber} value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))} onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ""))}
disabled={isLoading} className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono tracking-wider"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left tracking-widest text-sm" dir="ltr"
/> />
</div> </div>
@ -523,35 +602,45 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
<label htmlFor="reg-email" className="text-xs font-bold text-medical-gray-500">ایمیل (اختیاری)</label> <label htmlFor="reg-email" className="text-xs font-bold text-medical-gray-500">ایمیل (اختیاری)</label>
<input <input
id="reg-email" id="reg-email"
name="email"
type="email" type="email"
autoComplete="email" placeholder="name@example.com"
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
disabled={isLoading} className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left text-sm" dir="ltr"
/> />
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label htmlFor="reg-password" className="text-xs font-bold text-medical-gray-500">رمز عبور *</label> <label htmlFor="reg-pass" className="text-xs font-bold text-medical-gray-500">رمز عبور</label>
<div className="relative">
<input <input
id="reg-password" id="reg-pass"
name="new-password" type={showPassword ? "text" : "password"}
type="password"
autoComplete="new-password" autoComplete="new-password"
required
placeholder="حداقل ۶ کاراکتر"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
disabled={isLoading} className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 pr-3 pl-10 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left text-sm" dir="ltr"
/> />
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute left-2.5 top-1/2 -translate-y-1/2 p-1 text-medical-gray-400 hover:text-medical-gray-700 transition-colors cursor-pointer"
tabIndex={-1}
>
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
</div> </div>
<div className="flex justify-start pt-1"> <div className="flex justify-start pt-1">
<button <button
type="button" type="button"
onClick={() => setView("login")} onClick={() => setView("login")}
className="text-xs font-bold text-canina-blue hover:underline" className="text-xs font-bold text-canina-blue hover:underline cursor-pointer"
> >
بازگشت به ورود بازگشت به ورود
</button> </button>
@ -582,7 +671,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
<button <button
type="button" type="button"
onClick={() => setView("login")} onClick={() => setView("login")}
className="text-[10px] font-black text-canina-blue hover:underline" className="text-[10px] font-black text-canina-blue hover:underline cursor-pointer"
> >
برگشت به صفحه ورود برگشت به صفحه ورود
</button> </button>
@ -595,9 +684,10 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
maxLength={11} maxLength={11}
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹" placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber} value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))} onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ""))}
disabled={isLoading} disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pr-12 pl-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-lg text-left tracking-widest" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pr-12 pl-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-lg text-left font-mono tracking-widest"
dir="ltr"
/> />
</div> </div>
</div> </div>
@ -634,43 +724,31 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
<button <button
type="button" type="button"
onClick={() => setView("otp-phone")} onClick={() => setView("otp-phone")}
className="text-[10px] font-black text-canina-blue flex items-center gap-1 hover:underline" className="text-[10px] font-black text-canina-blue flex items-center gap-1 hover:underline cursor-pointer"
> >
<ArrowRight className="w-3 h-3" /> <ArrowRight className="w-3 h-3" />
تغییر شماره تغییر شماره
</button> </button>
</div> </div>
<div className="relative"> <div className="relative">
<Key className="absolute right-4 top-1/2 -translate-y-1/2 w-5 h-5 text-medical-gray-400" /> <Key className="absolute right-4 top-1/2 -translate-y-1/2 w-5 h-5 text-medical-gray-400 pointer-events-none" />
<input <input
ref={otpInputRef} ref={otpInputRef}
type="text" type="text"
inputMode="numeric"
autoComplete="one-time-code"
maxLength={5} maxLength={5}
placeholder="کد ۵ رقمی" placeholder="کد ۵ رقمی"
value={otpCode} value={otpCode}
onChange={(e) => setOtpCode(e.target.value.replace(/[^0-9]/g, ''))} onChange={(e) => handleOtpChange(e.target.value)}
disabled={isLoading} disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pr-12 pl-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-2xl text-center tracking-[0.5em]" className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pr-12 pl-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-center text-2xl font-mono tracking-[0.5em]"
dir="ltr"
/> />
</div> </div>
</div> <p className="text-[11px] text-medical-gray-400 text-center font-medium pt-1">
کد پیامک‌شده به صورت خودکار شناسایی و وارد می‌شود.
<div className="text-center"> </p>
{countdown > 0 ? (
<span className="text-xs font-bold text-medical-gray-400">
ارسال مجدد کد پس از {toPersian(countdown)} ثانیه
</span>
) : (
<button
type="button"
onClick={handleResendOtp}
disabled={isLoading}
className="text-xs font-black text-canina-blue hover:underline flex items-center gap-1.5 justify-center mx-auto"
>
<RefreshCw className="w-3.5 h-3.5" />
ارسال مجدد کد تایید
</button>
)}
</div> </div>
<button <button
@ -682,55 +760,77 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
<RefreshCw className="w-6 h-6 animate-spin" /> <RefreshCw className="w-6 h-6 animate-spin" />
) : ( ) : (
<> <>
<LogIn className="w-6 h-6" /> <ShieldCheck className="w-6 h-6" />
تایید کد و ورود تایید و ورود
</> </>
)} )}
</button> </button>
<div className="text-center pt-2">
{countdown > 0 ? (
<span className="text-xs font-bold text-medical-gray-400">
ارسال مجدد کد تا {toPersian(countdown.toString())} ثانیه دیگر
</span>
) : (
<button
type="button"
onClick={handleResendOtp}
className="text-xs font-black text-canina-blue hover:underline cursor-pointer"
>
ارسال مجدد کد تایید
</button>
)}
</div>
</motion.form> </motion.form>
)} )}
{/* Forgot Password - Enter Phone */}
{view === "forgot-password" && ( {view === "forgot-password" && (
<motion.form <motion.form
key="forgot-password-view" key="forgot-view"
initial={{ opacity: 0, x: 20 }} initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }} exit={{ opacity: 0, x: -20 }}
onSubmit={handleForgotSendOtp} onSubmit={handleForgotSendOtp}
className="space-y-4" className="space-y-4"
> >
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-4 text-center"> <div className="space-y-1">
<Lock className="w-8 h-8 text-amber-500 mx-auto mb-2" /> <label className="text-xs font-bold text-medical-gray-500">شماره موبایل حساب</label>
<p className="text-sm font-bold text-amber-700">برای بازیابی رمز عبور، کد تایید به شمارهتان ارسال می‌شود</p> <div className="relative">
</div> <Phone className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-medical-gray-400" />
<div className="space-y-2">
<label htmlFor="forgot-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل</label>
<input <input
id="forgot-phone" autoFocus
name="username"
type="tel" type="tel"
maxLength={11} maxLength={11}
autoComplete="username" required
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹" placeholder="۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber} value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))} onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ""))}
disabled={isLoading} className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 pr-11 pl-4 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono tracking-wider"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-left tracking-widest" dir="ltr"
/> />
</div> </div>
</div>
<div className="flex justify-start pt-1">
<button
type="button"
onClick={() => setView("login")}
className="text-xs font-bold text-canina-blue hover:underline cursor-pointer"
>
بازگشت به ورود
</button>
</div>
<button <button
type="submit" type="submit"
disabled={isLoading || phoneNumber.length < 11} disabled={isLoading || phoneNumber.length < 11}
className="w-full py-4 bg-canina-blue text-white rounded-xl font-black hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2" className="w-full py-4 bg-canina-blue text-white rounded-xl font-black hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2 cursor-pointer"
> >
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>ارسال کد بازیابی</>} {isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>ارسال کد بازیابی</>}
</button> </button>
<button type="button" onClick={() => setView("login")} className="w-full text-xs font-bold text-medical-gray-400 hover:text-canina-blue">بازگشت به ورود</button>
</motion.form> </motion.form>
)} )}
{/* Forgot Password - Enter OTP */}
{view === "forgot-otp" && ( {view === "forgot-otp" && (
<motion.form <motion.form
key="forgot-otp-view" key="forgot-otp-view"
@ -738,74 +838,86 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }} exit={{ opacity: 0, x: -20 }}
onSubmit={handleForgotVerifyOtp} onSubmit={handleForgotVerifyOtp}
className="space-y-5" className="space-y-4"
> >
<p className="text-sm font-bold text-medical-gray-600 text-center">کد ارسال شده به {toPersian(phoneNumber)} را وارد کنید</p> <div className="space-y-1">
<label className="text-xs font-bold text-medical-gray-500">کد تایید بازیابی</label>
<div className="relative"> <div className="relative">
<Key className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-medical-gray-400 pointer-events-none" />
<input <input
ref={otpInputRef} autoFocus
id="forgot-otp-code"
name="one-time-code"
type="text" type="text"
autoComplete="one-time-code"
inputMode="numeric" inputMode="numeric"
autoComplete="one-time-code"
maxLength={5} maxLength={5}
required
placeholder="کد ۵ رقمی"
value={otpCode} value={otpCode}
onChange={(e) => setOtpCode(e.target.value.replace(/[^0-9]/g, ''))} onChange={(e) => setOtpCode(toEnglishDigits(e.target.value).replace(/[^0-9]/g, "").slice(0, 5))}
disabled={isLoading} className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 pr-11 pl-4 focus:ring-2 focus:ring-canina-blue/20 outline-none text-center font-mono font-bold text-xl tracking-[0.4em]"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pl-4 pr-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-2xl text-center tracking-[0.5em]" dir="ltr"
/> />
</div> </div>
{countdown > 0 ? ( </div>
<p className="text-xs font-bold text-medical-gray-400 text-center">ارسال مجدد پس از {toPersian(countdown)} ثانیه</p>
) : ( <button
<button type="button" onClick={handleForgotSendOtp} disabled={isLoading} className="text-xs font-black text-canina-blue hover:underline flex items-center gap-1.5 justify-center mx-auto"> type="submit"
<RefreshCw className="w-3.5 h-3.5" />ارسال مجدد disabled={isLoading || otpCode.length < 5}
</button> className="w-full py-4 bg-canina-blue text-white rounded-xl font-black hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2 cursor-pointer"
)} >
<button type="submit" disabled={isLoading || otpCode.length < 5} className="w-full py-4 bg-canina-blue text-white rounded-xl font-black disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2"> {isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>تایید و ادامه</>}
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>تایید کد</>}
</button> </button>
</motion.form> </motion.form>
)} )}
{/* Reset Password */}
{view === "reset-password" && ( {view === "reset-password" && (
<motion.form <motion.form
key="reset-password-view" key="reset-pass-view"
initial={{ opacity: 0, x: 20 }} initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }} exit={{ opacity: 0, x: -20 }}
onSubmit={handleResetPassword} onSubmit={handleResetPassword}
className="space-y-4" className="space-y-4"
> >
<div className="bg-green-50 border border-green-200 rounded-2xl p-4 text-center"> <div className="space-y-1">
<p className="text-sm font-bold text-green-700">هویت تایید شد. رمز جدید خود را وارد کنید</p> <label className="text-xs font-bold text-medical-gray-500">رمز عبور جدید</label>
</div> <div className="relative">
<div className="space-y-2"> <Lock className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-medical-gray-400 pointer-events-none" />
<label htmlFor="new-password" className="text-xs font-bold text-medical-gray-500">رمز جدید</label>
<input <input
id="new-password" autoFocus
name="new-password" type={showNewPassword ? "text" : "password"}
type="password"
autoComplete="new-password" autoComplete="new-password"
required
placeholder="حداقل ۶ کاراکتر" placeholder="حداقل ۶ کاراکتر"
value={newPassword} value={newPassword}
onChange={(e) => setNewPassword(e.target.value)} onChange={(e) => setNewPassword(e.target.value)}
disabled={isLoading} className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 pr-11 pl-11 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left" dir="ltr"
/> />
<button
type="button"
onClick={() => setShowNewPassword(!showNewPassword)}
className="absolute left-3 top-1/2 -translate-y-1/2 p-1 text-medical-gray-400 hover:text-medical-gray-700 transition-colors cursor-pointer"
tabIndex={-1}
>
{showNewPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div> </div>
<button type="submit" disabled={isLoading || newPassword.length < 6} className="w-full py-4 bg-canina-blue text-white rounded-xl font-black disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2"> </div>
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>تغییر رمز عبور</>}
<button
type="submit"
disabled={isLoading || newPassword.length < 6}
className="w-full py-4 bg-canina-blue text-white rounded-xl font-black hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2 cursor-pointer"
>
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>ثبت رمز عبور جدید</>}
</button> </button>
</motion.form> </motion.form>
)} )}
</AnimatePresence> </AnimatePresence>
</div> </div>
</motion.div> </motion.div>
</> </div>
)} )}
</AnimatePresence> </AnimatePresence>
); );

View File

@ -123,7 +123,7 @@ export default function B2BPortal({ onClose }: { onClose: () => void }) {
try { try {
const api = (await import('../lib/services/api')).default; const api = (await import('../lib/services/api')).default;
await api.post('/wholesale/apply', { businessName: bName, licenseNumber: phone }); await api.post('/wholesale/apply', { businessName: bName, licenseNumber: phone });
alert('درخواست شما با موفقیت ثبت شد. کارشناسان پشتیبانی B2B کانینا به زودی جهت تایید تلفنی با شما تماس خواهند گرفت.'); alert('درخواست شما با موفقیت ثبت شد. کارشناسان پشتیبانی B2B کنینا به زودی جهت تایید تلفنی با شما تماس خواهند گرفت.');
form.reset(); form.reset();
} catch { } catch {
alert('خطا در ثبت درخواست. لطفاً دوباره تلاش کنید.'); alert('خطا در ثبت درخواست. لطفاً دوباره تلاش کنید.');

View File

@ -89,7 +89,7 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
</div> </div>
<div> <div>
<div className="font-black text-medical-gray-900 text-lg">{selectedPost.author}</div> <div className="font-black text-medical-gray-900 text-lg">{selectedPost.author}</div>
<div className="font-bold text-medical-gray-400">تیم تحقیق و توسعه کانینا آلمان</div> <div className="font-bold text-medical-gray-400">تیم تحقیق و توسعه کنینا آلمان</div>
</div> </div>
</div> </div>
<div className="flex gap-4"> <div className="flex gap-4">
@ -183,7 +183,7 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
مجله تخصصی <span className="text-canina-blue">سلامت پت‌ها</span> مجله تخصصی <span className="text-canina-blue">سلامت پت‌ها</span>
</h1> </h1>
<p className="text-sm sm:text-base text-medical-gray-500 font-medium mt-3 max-w-xl leading-relaxed"> <p className="text-sm sm:text-base text-medical-gray-500 font-medium mt-3 max-w-xl leading-relaxed">
آخرین یافته‌های علمی محققین کانینا آلمان در مورد تغذیه و سلامت پت‌ها. آخرین یافته‌های علمی محققین کنینا آلمان در مورد تغذیه و سلامت پت‌ها.
</p> </p>
</div> </div>
@ -226,7 +226,7 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
</div> </div>
<div className="text-sm"> <div className="text-sm">
<div className="font-black text-medical-gray-900">{blogs[0].author}</div> <div className="font-black text-medical-gray-900">{blogs[0].author}</div>
<div className="font-bold text-medical-gray-400">نویسنده کانینا</div> <div className="font-bold text-medical-gray-400">نویسنده کنینا</div>
</div> </div>
</div> </div>
<div className="flex items-center gap-2 text-canina-blue font-black"> <div className="flex items-center gap-2 text-canina-blue font-black">
@ -305,7 +305,7 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
C C
</div> </div>
<span className="text-xs font-black text-white/90 tracking-wider font-vazir"> <span className="text-xs font-black text-white/90 tracking-wider font-vazir">
خبرنامه اختصاصی پژوهشگاه کانینا آلمان خبرنامه اختصاصی پژوهشگاه کنینا آلمان
</span> </span>
<Sparkles className="w-3.5 h-3.5 text-canina-gold animate-pulse" /> <Sparkles className="w-3.5 h-3.5 text-canina-gold animate-pulse" />
</div> </div>
@ -316,7 +316,7 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
مشترک <span className="text-transparent bg-clip-text bg-gradient-to-r from-canina-gold via-white to-canina-teal">خبرنامه علمی</span> شوید مشترک <span className="text-transparent bg-clip-text bg-gradient-to-r from-canina-gold via-white to-canina-teal">خبرنامه علمی</span> شوید
</h3> </h3>
<p className="text-sm sm:text-base text-white/80 font-medium font-vazir leading-relaxed"> <p className="text-sm sm:text-base text-white/80 font-medium font-vazir leading-relaxed">
ماهانه یک ایمیل حاوی تازه‌ترین یافتـه‌های پزشکی، کاتالوگ‌های درمانی اختصاصی کانینا و کدهای تخفیف ویژه برای شما ارسال می‌شود. ماهانه یک ایمیل حاوی تازه‌ترین یافتـه‌های پزشکی، کاتالوگ‌های درمانی اختصاصی کنینا و کدهای تخفیف ویژه برای شما ارسال می‌شود.
</p> </p>
</div> </div>
@ -369,7 +369,7 @@ export default function BlogPage({ blogs = [] }: { blogs: BlogPost[] }) {
<div className="flex-1"> <div className="flex-1">
<h3 className="text-3xl lg:text-4xl font-black italic mb-6">علم آلمانی در خدمت سلامت پت شما</h3> <h3 className="text-3xl lg:text-4xl font-black italic mb-6">علم آلمانی در خدمت سلامت پت شما</h3>
<p className="text-white/80 mb-8 leading-relaxed max-w-xl"> <p className="text-white/80 mb-8 leading-relaxed max-w-xl">
تمامی مقالات این وبلاگ بر اساس کاتالوگ‌های رسمی و نتایج آزمایشگاهی کمپانی کانینا آلمان تدوین شده است. تمامی مقالات این وبلاگ بر اساس کاتالوگ‌های رسمی و نتایج آزمایشگاهی کمپانی کنینا آلمان تدوین شده است.
</p> </p>
<button <button
onClick={() => router.push('/shop')} onClick={() => router.push('/shop')}

View File

@ -152,7 +152,7 @@ export default function ContactFormClient() {
نشانی دفتر مرکزی نشانی دفتر مرکزی
</h4> </h4>
<p className="text-sm font-bold text-medical-gray-900 leading-relaxed"> <p className="text-sm font-bold text-medical-gray-900 leading-relaxed">
تهران، جردن، خیابان سعیدی، ساختمان کانینا، طبقه ۵، واحد ۱۹ تهران، جردن، خیابان سعیدی، ساختمان کنینا، طبقه ۵، واحد ۱۹
</p> </p>
</div> </div>
</div> </div>
@ -170,7 +170,7 @@ export default function ContactFormClient() {
<CheckCircle2 className="w-12 h-12 text-emerald-600 mx-auto" /> <CheckCircle2 className="w-12 h-12 text-emerald-600 mx-auto" />
<h4 className="text-lg font-black">پیام شما با موفقیت ثبت شد</h4> <h4 className="text-lg font-black">پیام شما با موفقیت ثبت شد</h4>
<p className="text-sm font-medium leading-relaxed"> <p className="text-sm font-medium leading-relaxed">
پیامک تایید به شماره شما ارسال شد. کارشناسان کانینا به زودی با شما تماس خواهند گرفت. پیامک تایید به شماره شما ارسال شد. کارشناسان کنینا به زودی با شما تماس خواهند گرفت.
</p> </p>
<button <button
onClick={() => setSuccess(false)} onClick={() => setSuccess(false)}

View File

@ -45,7 +45,7 @@ export const ServerErrorPage = ({ onRetry }: { onRetry: () => void }) => {
<h1 className="text-4xl font-black text-medical-gray-900 mb-4">خطای داخلی سیستم</h1> <h1 className="text-4xl font-black text-medical-gray-900 mb-4">خطای داخلی سیستم</h1>
<p className="text-medical-gray-500 mb-12 font-bold text-lg"> <p className="text-medical-gray-500 mb-12 font-bold text-lg">
ارتباط با دیتابیس کانینا با مشکل مواجه شده است. در حال بررسی موضوع هستیم. ارتباط با دیتابیس کنینا با مشکل مواجه شده است. در حال بررسی موضوع هستیم.
</p> </p>
<div className="flex flex-col sm:flex-row gap-4 justify-center"> <div className="flex flex-col sm:flex-row gap-4 justify-center">

View File

@ -155,7 +155,7 @@ export default function Footer({
</div> </div>
<div> <div>
<p className="text-[11px] text-slate-400 font-extrabold uppercase mb-1">{getText('footer_address_label', "دفتر مرکزی")}</p> <p className="text-[11px] text-slate-400 font-extrabold uppercase mb-1">{getText('footer_address_label', "دفتر مرکزی")}</p>
<p className="text-sm font-bold leading-relaxed text-slate-300">{getText('CONTACT_ADDRESS', getText('footer_address', "تهران، جردن، خیابان سعیدی، ساختمان کانینا، طبقه ۵، واحد ۱۹"))}</p> <p className="text-sm font-bold leading-relaxed text-slate-300">{getText('CONTACT_ADDRESS', getText('footer_address', "تهران، جردن، خیابان سعیدی، ساختمان کنینا، طبقه ۵، واحد ۱۹"))}</p>
</div> </div>
</div> </div>
</div> </div>
@ -184,7 +184,7 @@ export default function Footer({
</div> </div>
<div className="pt-12 border-t border-white/5 flex flex-col md:flex-row items-center justify-between gap-6 text-[10px] font-bold uppercase tracking-[0.3em] font-shabnam"> <div className="pt-12 border-t border-white/5 flex flex-col md:flex-row items-center justify-between gap-6 text-[10px] font-bold uppercase tracking-[0.3em] font-shabnam">
<div className="text-slate-400 font-shabnam">{getText('footer_copyright', "© ۲۰۲۶ کانینا ایران. تمامی حقوق محفوظ است.")}</div> <div className="text-slate-400 font-shabnam">{getText('footer_copyright', "© ۲۰۲۶ کنینا ایران. تمامی حقوق محفوظ است.")}</div>
<div className="flex gap-4 sm:gap-8 font-shabnam text-slate-400 items-center"> <div className="flex gap-4 sm:gap-8 font-shabnam text-slate-400 items-center">
<Link href="/about" className="font-extrabold hover:text-white transition-colors whitespace-nowrap">{getText('footer_about', "درباره ما")}</Link> <Link href="/about" className="font-extrabold hover:text-white transition-colors whitespace-nowrap">{getText('footer_about', "درباره ما")}</Link>
<Link href="/contact" className="font-extrabold hover:text-white transition-colors whitespace-nowrap">{getText('footer_contact', "تماس با ما")}</Link> <Link href="/contact" className="font-extrabold hover:text-white transition-colors whitespace-nowrap">{getText('footer_contact', "تماس با ما")}</Link>

View File

@ -149,7 +149,7 @@ export default function Header({
{(getText('BRAND_LOGO_URL', '') || getText('site_logo', '')) ? ( {(getText('BRAND_LOGO_URL', '') || getText('site_logo', '')) ? (
<img <img
src={getText('BRAND_LOGO_URL', '') || getText('site_logo', '')} src={getText('BRAND_LOGO_URL', '') || getText('site_logo', '')}
alt={getText('brand_name_fa', "کانینا ایران")} alt={getText('brand_name_fa', "کنینا ایران")}
className="h-10 sm:h-12 w-auto max-w-[140px] sm:max-w-[180px] object-contain shrink-0" className="h-10 sm:h-12 w-auto max-w-[140px] sm:max-w-[180px] object-contain shrink-0"
/> />
) : ( ) : (
@ -410,7 +410,7 @@ export default function Header({
<div className="absolute top-full right-0 w-56 bg-white border border-medical-gray-200 shadow-xl rounded-xl p-2 z-50 space-y-1 text-xs"> <div className="absolute top-full right-0 w-56 bg-white border border-medical-gray-200 shadow-xl rounded-xl p-2 z-50 space-y-1 text-xs">
<Link href="/wiki" className="flex items-center gap-2 px-3 py-2 hover:bg-canina-blue/5 hover:text-canina-blue rounded-lg font-bold"> <Link href="/wiki" className="flex items-center gap-2 px-3 py-2 hover:bg-canina-blue/5 hover:text-canina-blue rounded-lg font-bold">
<BookOpen className="w-4 h-4 text-canina-blue" /> <BookOpen className="w-4 h-4 text-canina-blue" />
دانشنامه علمی کانینا دانشنامه علمی کنینا
</Link> </Link>
<Link href="/blog" className="flex items-center gap-2 px-3 py-2 hover:bg-canina-blue/5 hover:text-canina-blue rounded-lg font-bold"> <Link href="/blog" className="flex items-center gap-2 px-3 py-2 hover:bg-canina-blue/5 hover:text-canina-blue rounded-lg font-bold">
<FileText className="w-4 h-4 text-canina-blue" /> <FileText className="w-4 h-4 text-canina-blue" />
@ -455,14 +455,14 @@ export default function Header({
onMouseLeave={() => setActiveDropdown(null)} onMouseLeave={() => setActiveDropdown(null)}
> >
<button className="flex items-center gap-1.5 px-3 py-2 rounded-xl text-medical-gray-700 hover:bg-medical-gray-100 hover:text-canina-blue transition-all"> <button className="flex items-center gap-1.5 px-3 py-2 rounded-xl text-medical-gray-700 hover:bg-medical-gray-100 hover:text-canina-blue transition-all">
<span>درباره کانینا و تماس</span> <span>درباره کنینا و تماس</span>
<ChevronDown className="w-3.5 h-3.5" /> <ChevronDown className="w-3.5 h-3.5" />
</button> </button>
{activeDropdown === 'company' && ( {activeDropdown === 'company' && (
<div className="absolute top-full right-0 w-60 bg-white border border-medical-gray-200 shadow-xl rounded-xl p-2 z-50 space-y-1 text-xs"> <div className="absolute top-full right-0 w-60 bg-white border border-medical-gray-200 shadow-xl rounded-xl p-2 z-50 space-y-1 text-xs">
<Link href="/about" className="flex items-center gap-2 px-3 py-2 hover:bg-canina-blue/5 hover:text-canina-blue rounded-lg font-bold"> <Link href="/about" className="flex items-center gap-2 px-3 py-2 hover:bg-canina-blue/5 hover:text-canina-blue rounded-lg font-bold">
<Award className="w-4 h-4 text-canina-blue" /> <Award className="w-4 h-4 text-canina-blue" />
درباره کمپانی کانینا آلمان درباره کمپانی کنینا آلمان
</Link> </Link>
<Link href="/trust-seals" className="flex items-center gap-2 px-3 py-2 hover:bg-canina-blue/5 hover:text-canina-blue rounded-lg font-bold text-emerald-600"> <Link href="/trust-seals" className="flex items-center gap-2 px-3 py-2 hover:bg-canina-blue/5 hover:text-canina-blue rounded-lg font-bold text-emerald-600">
<ShieldCheck className="w-4 h-4 text-emerald-600" /> <ShieldCheck className="w-4 h-4 text-emerald-600" />
@ -517,7 +517,7 @@ export default function Header({
<div className="space-y-2 text-sm font-black"> <div className="space-y-2 text-sm font-black">
<Link href="/shop" onClick={() => setIsMobileMenuOpen(false)} className="block p-3 rounded-xl bg-medical-gray-50 text-medical-gray-900"> <Link href="/shop" onClick={() => setIsMobileMenuOpen(false)} className="block p-3 rounded-xl bg-medical-gray-50 text-medical-gray-900">
🛒 محصولات تخصصی کانینا 🛒 محصولات تخصصی کنینا
</Link> </Link>
<Link href="/catalog" onClick={() => setIsMobileMenuOpen(false)} className="block p-3 rounded-xl hover:bg-medical-gray-50 text-medical-gray-800"> <Link href="/catalog" onClick={() => setIsMobileMenuOpen(false)} className="block p-3 rounded-xl hover:bg-medical-gray-50 text-medical-gray-800">
📑 کاتالوگ آنلاین و دوز مصرفی 📑 کاتالوگ آنلاین و دوز مصرفی

View File

@ -49,7 +49,7 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
}, []); }, []);
const defaultTitle = banners.length > 0 && banners[0].title ? banners[0].title : "تخصص آلمانی در خدمت\nسلامت پت‌های خانگی"; const defaultTitle = banners.length > 0 && banners[0].title ? banners[0].title : "تخصص آلمانی در خدمت\nسلامت پت‌های خانگی";
const defaultSubtitle = banners.length > 0 && banners[0].subtitle ? banners[0].subtitle : 'از سال ۱۹۸۴، شرکت Canina pharma GmbH در برگیش گلادباخ آلمان، با بهره‌گیری از مواد اولیه طبیعی و فرآیندهای پیشرفته، استاندارد طلایی مکمل‌های دامپزشکی را تعریف می‌کند. کانینا نماینده رسمی این برند در ایران است.'; const defaultSubtitle = banners.length > 0 && banners[0].subtitle ? banners[0].subtitle : 'از سال ۱۹۸۴، شرکت Canina pharma GmbH در برگیش گلادباخ آلمان، با بهره‌گیری از مواد اولیه طبیعی و فرآیندهای پیشرفته، استاندارد طلایی مکمل‌های دامپزشکی را تعریف می‌کند. کنینا نماینده رسمی این برند در ایران است.';
const title = (mounted && isInitialized ? getText('hero_title', '') : '') || defaultTitle; const title = (mounted && isInitialized ? getText('hero_title', '') : '') || defaultTitle;
const subtitle = (mounted && isInitialized ? getText('hero_desc', '') : '') || defaultSubtitle; const subtitle = (mounted && isInitialized ? getText('hero_desc', '') : '') || defaultSubtitle;

View File

@ -82,7 +82,7 @@ export default function IngredientWiki() {
علم در خدمت کیفیت علم در خدمت کیفیت
</div> </div>
<h2 className="text-4xl lg:text-6xl font-black text-medical-gray-900 leading-tight md:max-w-4xl"> <h2 className="text-4xl lg:text-6xl font-black text-medical-gray-900 leading-tight md:max-w-4xl">
دانشنامه مواد <span className="text-canina-blue italic">نایاب و ارگانیک</span> کانینا دانشنامه مواد <span className="text-canina-blue italic">نایاب و ارگانیک</span> کنینا
</h2> </h2>
<p className="mt-6 text-lg text-medical-gray-500 max-w-2xl leading-relaxed"> <p className="mt-6 text-lg text-medical-gray-500 max-w-2xl leading-relaxed">
ما از مواد اولیه‌ای استفاده می‌کنیم که در سطح دارویی فرآوری شده‌اند. در اینجا با قدرت بیولوژیک این ترکیبات آشنا شوید. ما از مواد اولیه‌ای استفاده می‌کنیم که در سطح دارویی فرآوری شده‌اند. در اینجا با قدرت بیولوژیک این ترکیبات آشنا شوید.

View File

@ -203,7 +203,7 @@ export default function LoginModal({ isOpen, onClose, onLogin, petName, isAdviso
<ShieldCheck className="w-10 h-10 text-canina-blue" /> <ShieldCheck className="w-10 h-10 text-canina-blue" />
</div> </div>
<h3 className="text-3xl font-black text-medical-gray-900 mb-2 italic"> <h3 className="text-3xl font-black text-medical-gray-900 mb-2 italic">
ورود به کانینا ورود به کنینا
</h3> </h3>
{isAdvisorContext ? ( {isAdvisorContext ? (
<p className="text-medical-gray-500 font-bold leading-relaxed px-2 text-sm"> <p className="text-medical-gray-500 font-bold leading-relaxed px-2 text-sm">

View File

@ -23,7 +23,7 @@ export default function MaintenancePage() {
</h1> </h1>
<p className="text-sm sm:text-base text-medical-gray-300 leading-relaxed"> <p className="text-sm sm:text-base text-medical-gray-300 leading-relaxed">
وب‌سایت کانینا ایران جهت ارتقای زیرساخت‌ها و ارائه خدمات بهتر به شما سرپرستان و دامپزشکان محترم به صورت موقت در دست به‌روزرسانی است. از شکیبایی شما سپاسگزاریم. وب‌سایت کنینا ایران جهت ارتقای زیرساخت‌ها و ارائه خدمات بهتر به شما سرپرستان و دامپزشکان محترم به صورت موقت در دست به‌روزرسانی است. از شکیبایی شما سپاسگزاریم.
</p> </p>
<div className="pt-4 flex flex-col sm:flex-row items-center justify-center gap-4"> <div className="pt-4 flex flex-col sm:flex-row items-center justify-center gap-4">

View File

@ -24,7 +24,7 @@ interface OrderDetailsModalProps {
export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetailsModalProps) { export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetailsModalProps) {
if (!order) return null; if (!order) return null;
const getProductName = (p: Record<string, unknown> | undefined) => String(p?.nameFa || p?.name || p?.nameEn || 'محصول کانینا'); const getProductName = (p: Record<string, unknown> | undefined) => String(p?.nameFa || p?.name || p?.nameEn || 'محصول کنینا');
const getProductImage = (p: Record<string, unknown> | undefined) => String(p?.imageUrl || p?.image || '/products/product-placeholder.png'); const getProductImage = (p: Record<string, unknown> | undefined) => String(p?.imageUrl || p?.image || '/products/product-placeholder.png');
const displayTrackingCode = String(order.trackingNumber || (typeof order.id === 'string' ? order.id.substring(0, 8) : '') || 'CN-0000'); const displayTrackingCode = String(order.trackingNumber || (typeof order.id === 'string' ? order.id.substring(0, 8) : '') || 'CN-0000');
@ -76,7 +76,7 @@ export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetai
<html dir="rtl" lang="fa"> <html dir="rtl" lang="fa">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>فاکتور رسمی کانینا - ${displayTrackingCode}</title> <title>فاکتور رسمی کنینا - ${displayTrackingCode}</title>
<style> <style>
@font-face { @font-face {
font-family: 'Vazirmatn'; font-family: 'Vazirmatn';
@ -104,7 +104,7 @@ export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetai
<div class="invoice-card"> <div class="invoice-card">
<div class="header"> <div class="header">
<div> <div>
<div class="logo-text">CANINA | کانینا</div> <div class="logo-text">CANINA | کنینا</div>
<div class="sub-logo">مکمل‌های تخصصی دامپزشکی آلمان</div> <div class="sub-logo">مکمل‌های تخصصی دامپزشکی آلمان</div>
</div> </div>
<div style="text-align: left;"> <div style="text-align: left;">
@ -167,7 +167,7 @@ export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetai
<div class="footer"> <div class="footer">
این فاکتور رسمی به صورت هوشمند و سیستمی صادر شده و فاقد نیاز به مهر و امضای فیزیکی است.<br/> این فاکتور رسمی به صورت هوشمند و سیستمی صادر شده و فاقد نیاز به مهر و امضای فیزیکی است.<br/>
پشتیبانی کانینا آلمان: ۰۲۱-۸۸۸۸۸۸۸۸ | www.canina.ir پشتیبانی کنینا آلمان: ۰۲۱-۸۸۸۸۸۸۸۸ | www.canina.ir
</div> </div>
</div> </div>
@ -355,7 +355,7 @@ export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetai
className="flex-1 py-4 bg-canina-blue text-white rounded-2xl font-black text-sm flex items-center justify-center gap-3 hover:bg-medical-gray-900 transition-all shadow-lg shadow-canina-blue/20" className="flex-1 py-4 bg-canina-blue text-white rounded-2xl font-black text-sm flex items-center justify-center gap-3 hover:bg-medical-gray-900 transition-all shadow-lg shadow-canina-blue/20"
> >
<Download className="w-5 h-5" /> <Download className="w-5 h-5" />
دانلود و چاپ فاکتور رسمی کانینا دانلود و چاپ فاکتور رسمی کنینا
</button> </button>
</div> </div>
</motion.div> </motion.div>

View File

@ -50,7 +50,7 @@ export default function OrderTracking() {
</button> </button>
<div className="flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest overflow-x-auto whitespace-nowrap pb-2"> <div className="flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest overflow-x-auto whitespace-nowrap pb-2">
<span className="cursor-pointer hover:text-canina-blue" onClick={() => router.push('/')}>کانینا</span> <span className="cursor-pointer hover:text-canina-blue" onClick={() => router.push('/')}>کنینا</span>
<ChevronLeft className="w-2.5 h-2.5" /> <ChevronLeft className="w-2.5 h-2.5" />
<span className="text-canina-blue">پیگیری سفارش</span> <span className="text-canina-blue">پیگیری سفارش</span>
</div> </div>

View File

@ -1090,9 +1090,9 @@ export default function PetProfile({ initialView }: {
<div className="flex justify-center mb-8"> <div className="flex justify-center mb-8">
<div className="w-20 h-20 border-4 border-canina-blue rounded-full flex items-center justify-center text-canina-blue font-black text-2xl rotate-12 shadow-2xl">آلمان</div> <div className="w-20 h-20 border-4 border-canina-blue rounded-full flex items-center justify-center text-canina-blue font-black text-2xl rotate-12 shadow-2xl">آلمان</div>
</div> </div>
<h4 className="text-2xl font-black text-medical-gray-900 mb-4 italic">ضمانت اصالت و سلامت کانینا آلمان</h4> <h4 className="text-2xl font-black text-medical-gray-900 mb-4 italic">ضمانت اصالت و سلامت کنینا آلمان</h4>
<p className="text-sm text-medical-gray-500 font-medium leading-relaxed max-w-2xl mx-auto italic"> <p className="text-sm text-medical-gray-500 font-medium leading-relaxed max-w-2xl mx-auto italic">
این گزارش توسط سیستم هوش مصنوعی کانینا و با انطباق کامل بر پروتکل‌های درمانی Canina Pharma GmbH آلمان تولید شده است. تمامی مکمل‌های پیشنهادی دارای استاندارد دارویی (Pharmaceutical Grade) می‌باشند. این گزارش توسط سیستم هوش مصنوعی کنینا و با انطباق کامل بر پروتکل‌های درمانی Canina Pharma GmbH آلمان تولید شده است. تمامی مکمل‌های پیشنهادی دارای استاندارد دارویی (Pharmaceutical Grade) می‌باشند.
</p> </p>
</div> </div>

View File

@ -117,7 +117,7 @@ export default function PrescriptionUploadModal({ isOpen, onClose }: Prescriptio
</div> </div>
<h4 className="text-lg font-black text-medical-gray-900">نسخه با موفقیت ثبت شد</h4> <h4 className="text-lg font-black text-medical-gray-900">نسخه با موفقیت ثبت شد</h4>
<p className="text-xs text-medical-gray-500 max-w-sm mx-auto leading-relaxed"> <p className="text-xs text-medical-gray-500 max-w-sm mx-auto leading-relaxed">
کارشناسان و دامپزشکان کانینا نسخه شما را بررسی کرده و ظرف حداکثر ۱۵ دقیقه جهت هماهنگی ارسال دارو با شما تماس خواهند گرفت. کارشناسان و دامپزشکان کنینا نسخه شما را بررسی کرده و ظرف حداکثر ۱۵ دقیقه جهت هماهنگی ارسال دارو با شما تماس خواهند گرفت.
</p> </p>
<button <button
onClick={handleClose} onClick={handleClose}

View File

@ -14,7 +14,7 @@ interface SafeImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>,
export default function SafeImage({ export default function SafeImage({
src, src,
alt = "تصویر مکمل کانینا", alt = "تصویر مکمل کنینا",
className, className,
imgClassName, imgClassName,
fallbackText = "در حال بروزرسانی تصویر..." fallbackText = "در حال بروزرسانی تصویر..."

View File

@ -181,9 +181,9 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
<Sparkle className="w-4 h-4 text-canina-blue animate-spin" /> <Sparkle className="w-4 h-4 text-canina-blue animate-spin" />
<span suppressHydrationWarning className="text-canina-blue text-xs font-black uppercase tracking-widest font-vazir">{getText('advisor_badge', "سیستم پایش و تجویز هوشمند")}</span> <span suppressHydrationWarning className="text-canina-blue text-xs font-black uppercase tracking-widest font-vazir">{getText('advisor_badge', "سیستم پایش و تجویز هوشمند")}</span>
</motion.div> </motion.div>
<h2 suppressHydrationWarning className="text-4xl lg:text-5xl font-black text-medical-gray-900 mb-4 font-lalezar tracking-wide">{getText('advisor_title', "دستیار سلامت و الگوریتم پایش کانینا")}</h2> <h2 suppressHydrationWarning className="text-4xl lg:text-5xl font-black text-medical-gray-900 mb-4 font-lalezar tracking-wide">{getText('advisor_title', "دستیار سلامت و الگوریتم پایش کنینا")}</h2>
<p suppressHydrationWarning className="text-medical-gray-600 font-medium text-base font-shabnam max-w-2xl mx-auto leading-relaxed"> <p suppressHydrationWarning className="text-medical-gray-600 font-medium text-base font-shabnam max-w-2xl mx-auto leading-relaxed">
{getText('advisor_subtitle', "با ثبت اطلاعات همدم خود در ۴ قدم ساده، از الگوریتم علمی کانینا برای تجویز دقیق مکمل و پایش سلامت بهره‌مند شوید.")} {getText('advisor_subtitle', "با ثبت اطلاعات همدم خود در ۴ قدم ساده، از الگوریتم علمی کنینا برای تجویز دقیق مکمل و پایش سلامت بهره‌مند شوید.")}
</p> </p>
<div className="mt-4 flex justify-center"> <div className="mt-4 flex justify-center">
@ -213,7 +213,7 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
<h3 className="text-base sm:text-lg font-black text-medical-gray-900 sm:mb-2 font-vazir">چرا ثبت شناسنامه؟</h3> <h3 className="text-base sm:text-lg font-black text-medical-gray-900 sm:mb-2 font-vazir">چرا ثبت شناسنامه؟</h3>
</div> </div>
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir"> <p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
مکمل‌های دارویی کانینا غلیظ و تخصصی هستند؛ الگوریتم هوشمند ما بر اساس سن، وزن و جثه پت شما، دوز دقیق مصرفی روزانه را محاسبه می‌کند. مکمل‌های دارویی کنینا غلیظ و تخصصی هستند؛ الگوریتم هوشمند ما بر اساس سن، وزن و جثه پت شما، دوز دقیق مصرفی روزانه را محاسبه می‌کند.
</p> </p>
</div> </div>
<div className="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir"> <div className="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">

View File

@ -7,7 +7,7 @@ export default function TickerBanner() {
const getText = useSettingsStore((state) => state.getText); const getText = useSettingsStore((state) => state.getText);
const announcementText = getText( const announcementText = getText(
"shipping_notice", "shipping_notice",
"ارسال رایگان برای سفارش‌های بالای ۱۵۰ هزار تومان — گارانتی اصالت کانینا آلمان — مشاوره تخصصی مکمل‌های دارویی سگ و گربه با کادر دامپزشکان مجرب" "ارسال رایگان برای سفارش‌های بالای ۱۵۰ هزار تومان — گارانتی اصالت کنینا آلمان — مشاوره تخصصی مکمل‌های دارویی سگ و گربه با کادر دامپزشکان مجرب"
); );
return ( return (

View File

@ -37,7 +37,7 @@ export default function Tooltip({ termKey, children, onWikiNavigate }: TooltipPr
> >
<div className="flex items-center gap-2 mb-2 text-canina-blue"> <div className="flex items-center gap-2 mb-2 text-canina-blue">
<Info className="w-4 h-4" /> <Info className="w-4 h-4" />
<span className="font-black text-[11px] uppercase tracking-widest text-canina-blue">دانشنامه علمی کانینا</span> <span className="font-black text-[11px] uppercase tracking-widest text-canina-blue">دانشنامه علمی کنینا</span>
</div> </div>
<h5 className="font-black text-base text-medical-gray-900 mb-2 leading-snug">{termData.term}</h5> <h5 className="font-black text-base text-medical-gray-900 mb-2 leading-snug">{termData.term}</h5>
<p className="text-medical-gray-600 text-xs leading-relaxed mb-4 font-bold"> <p className="text-medical-gray-600 text-xs leading-relaxed mb-4 font-bold">

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import React, { useState } from "react"; import React, { useState } from "react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { User, ShoppingBag, Wallet, MapPin, LogOut, ChevronRight, Package, Calendar, UserCircle, ShoppingCart, Trash2, Edit2, Phone, Hash, CheckCircle2, ArrowUpCircle, ArrowDownCircle, Info, Clock, CheckCircle, Heart, Sparkles, MessageSquare, Send, Stethoscope, FileText } from "lucide-react"; import { User, ShoppingBag, Wallet, MapPin, LogOut, ChevronRight, Package, Calendar, UserCircle, ShoppingCart, Trash2, Edit2, Phone, Hash, CheckCircle2, ArrowUpCircle, ArrowDownCircle, Info, Clock, CheckCircle, Heart, Sparkles, MessageSquare, Send, Stethoscope, FileText, Lock, Key, Eye, EyeOff, ShieldCheck } from "lucide-react";
import { OrderRowSkeleton } from "./Skeleton"; import { OrderRowSkeleton } from "./Skeleton";
import { useUserStore, Address } from "../lib/store/userStore"; import { useUserStore, Address } from "../lib/store/userStore";
import { useCartStore } from "../lib/store/cartStore"; import { useCartStore } from "../lib/store/cartStore";
@ -107,6 +107,48 @@ export default function UserDashboard() {
} }
}; };
// Password Management State
const [currentPassword, setCurrentPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [showCurrentPass, setShowCurrentPass] = useState(false);
const [showNewPass, setShowNewPass] = useState(false);
const [showConfirmPass, setShowConfirmPass] = useState(false);
const [isSavingPassword, setIsSavingPassword] = useState(false);
const handleSavePassword = async (e: React.FormEvent) => {
e.preventDefault();
if (!newPassword || newPassword.length < 6) {
toast.error("رمز عبور جدید باید حداقل ۶ کاراکتر باشد");
return;
}
if (newPassword !== confirmPassword) {
toast.error("رمز عبور جدید و تکرار آن یکسان نیستند");
return;
}
if (profile.hasPassword && !currentPassword) {
toast.error("لطفاً رمز عبور فعلی خود را وارد کنید");
return;
}
setIsSavingPassword(true);
try {
await updateProfile({
password: newPassword.trim(),
currentPassword: profile.hasPassword ? currentPassword.trim() : undefined,
});
toast.success(profile.hasPassword ? "رمز عبور با موفقیت تغییر کرد" : "رمز عبور با موفقیت برای حساب شما ثبت شد");
setCurrentPassword("");
setNewPassword("");
setConfirmPassword("");
} catch (err: unknown) {
const errObj = err as { message?: string };
toast.error(errObj.message || "خطا در ثبت رمز عبور");
} finally {
setIsSavingPassword(false);
}
};
const handleLogout = () => { const handleLogout = () => {
logout(); logout();
router.push('/'); router.push('/');
@ -340,6 +382,137 @@ export default function UserDashboard() {
)} )}
</div> </div>
</form> </form>
{/* Password & Security Section */}
<div className="mt-12 pt-8 border-t border-medical-gray-100">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-6">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-2xl bg-canina-blue/10 text-canina-blue flex items-center justify-center">
<Lock className="w-5 h-5" />
</div>
<div>
<h4 className="text-lg font-black text-medical-gray-900 italic">
{profile.hasPassword ? "تغییر رمز عبور حساب" : "تعریف رمز عبور برای حساب کاربری"}
</h4>
<p className="text-xs font-medium text-medical-gray-400">
{profile.hasPassword
? "برای افزایش امنیت می‌توانید رمز عبور ورود خود را تغییر دهید."
: "با تعیین رمز عبور می‌توانید در دفعات بعدی علاوه بر پیامک، با رمز عبور نیز وارد شوید."}
</p>
</div>
</div>
<span
className={cn(
"px-3 py-1 rounded-full text-[11px] font-bold self-start sm:self-auto",
profile.hasPassword
? "bg-green-50 text-green-700 border border-green-200"
: "bg-amber-50 text-amber-700 border border-amber-200"
)}
>
{profile.hasPassword ? "رمز عبور فعال است" : "ورود فقط با OTP"}
</span>
</div>
<form onSubmit={handleSavePassword} className="bg-medical-gray-50/50 p-6 sm:p-8 rounded-[2rem] border border-medical-gray-100 space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{profile.hasPassword && (
<div className="space-y-1.5">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block pr-2">
رمز عبور فعلی *
</label>
<div className="relative">
<input
type={showCurrentPass ? "text" : "password"}
autoComplete="current-password"
required
placeholder="رمز فعلی..."
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
className="w-full bg-white border border-medical-gray-200 rounded-xl py-3 pr-3 pl-10 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono"
dir="ltr"
/>
<button
type="button"
onClick={() => setShowCurrentPass(!showCurrentPass)}
className="absolute left-2.5 top-1/2 -translate-y-1/2 p-1 text-medical-gray-400 hover:text-medical-gray-700 transition-colors cursor-pointer"
tabIndex={-1}
>
{showCurrentPass ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
</div>
)}
<div className="space-y-1.5">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block pr-2">
{profile.hasPassword ? "رمز عبور جدید *" : "رمز عبور دلخواه *"}
</label>
<div className="relative">
<input
type={showNewPass ? "text" : "password"}
autoComplete="new-password"
required
placeholder="حداقل ۶ کاراکتر"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
className="w-full bg-white border border-medical-gray-200 rounded-xl py-3 pr-3 pl-10 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono"
dir="ltr"
/>
<button
type="button"
onClick={() => setShowNewPass(!showNewPass)}
className="absolute left-2.5 top-1/2 -translate-y-1/2 p-1 text-medical-gray-400 hover:text-medical-gray-700 transition-colors cursor-pointer"
tabIndex={-1}
>
{showNewPass ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
</div>
<div className="space-y-1.5">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block pr-2">
تکرار رمز عبور *
</label>
<div className="relative">
<input
type={showConfirmPass ? "text" : "password"}
autoComplete="new-password"
required
placeholder="تکرار رمز عبور"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className="w-full bg-white border border-medical-gray-200 rounded-xl py-3 pr-3 pl-10 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left font-mono"
dir="ltr"
/>
<button
type="button"
onClick={() => setShowConfirmPass(!showConfirmPass)}
className="absolute left-2.5 top-1/2 -translate-y-1/2 p-1 text-medical-gray-400 hover:text-medical-gray-700 transition-colors cursor-pointer"
tabIndex={-1}
>
{showConfirmPass ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
</div>
</div>
<div className="flex justify-end pt-3">
<button
type="submit"
disabled={isSavingPassword || newPassword.length < 6 || !confirmPassword}
className="px-8 py-3.5 bg-canina-blue hover:bg-indigo-700 text-white rounded-xl font-black text-xs transition-all shadow-md shadow-canina-blue/15 flex items-center gap-2 cursor-pointer disabled:opacity-50"
>
{isSavingPassword ? (
<span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
) : (
<ShieldCheck className="w-4 h-4" />
)}
{profile.hasPassword ? "بروزرسانی رمز عبور" : "ثبت رمز عبور"}
</button>
</div>
</form>
</div>
</div> </div>
)} )}

View File

@ -107,7 +107,7 @@ export default function VetGallery({ testimonials = [] }: { testimonials?: Testi
{getText('videos_title', "مشاوره ویدئویی")} <span className="text-canina-blue underline decoration-4 decoration-canina-blue/20 underline-offset-8">{getText('videos_title_highlight', "دوست دامپزشک پت شما")}</span> {getText('videos_title', "مشاوره ویدئویی")} <span className="text-canina-blue underline decoration-4 decoration-canina-blue/20 underline-offset-8">{getText('videos_title_highlight', "دوست دامپزشک پت شما")}</span>
</h2> </h2>
<p className="text-lg text-medical-gray-500 font-medium mt-6"> <p className="text-lg text-medical-gray-500 font-medium mt-6">
{getText('videos_desc', "تیم علمی و فرموله‌کنندگان کمپانی کانینا در آلمان مستقیماً با شما صحبت می‌کنند؛ آشنایی با نحوه مصرف، مزایای بالینی و دانش پزشکی پشت هر محصول.")} {getText('videos_desc', "تیم علمی و فرموله‌کنندگان کمپانی کنینا در آلمان مستقیماً با شما صحبت می‌کنند؛ آشنایی با نحوه مصرف، مزایای بالینی و دانش پزشکی پشت هر محصول.")}
</p> </p>
</div> </div>
<Link <Link

View File

@ -85,7 +85,7 @@ export default function VideosPage() {
Video Academy Video Academy
</div> </div>
<h1 className="text-4xl lg:text-6xl font-black text-white italic leading-tight"> <h1 className="text-4xl lg:text-6xl font-black text-white italic leading-tight">
آکادمی ویدئویی <span className="text-canina-blue">کانینا</span> آکادمی ویدئویی <span className="text-canina-blue">کنینا</span>
</h1> </h1>
</div> </div>
<button <button

View File

@ -8,9 +8,9 @@ describe('Footer', () => {
it('renders footer brand text and standard layout elements', () => { it('renders footer brand text and standard layout elements', () => {
render(<Footer onNavigate={vi.fn()} onShopNavigate={vi.fn()} onB2BOpen={vi.fn()} />); render(<Footer onNavigate={vi.fn()} onShopNavigate={vi.fn()} onB2BOpen={vi.fn()} />);
expect(screen.getByText('کانینا ایران')).toBeInTheDocument(); expect(screen.getByText('کنینا ایران')).toBeInTheDocument();
expect(screen.getByText('نماینده رسمی در ایران')).toBeInTheDocument(); expect(screen.getByText('نماینده رسمی در ایران')).toBeInTheDocument();
expect(screen.getByText('تهران، جردن، خیابان سعیدی، ساختمان کانینا، طبقه ۵، واحد ۱۹')).toBeInTheDocument(); expect(screen.getByText('تهران، جردن، خیابان سعیدی، ساختمان کنینا، طبقه ۵، واحد ۱۹')).toBeInTheDocument();
}); });
it('triggers onNavigate and onShopNavigate when quick links are clicked', () => { it('triggers onNavigate and onShopNavigate when quick links are clicked', () => {

View File

@ -121,7 +121,7 @@ export default function CatalogPageSpread({
<div className="space-y-2"> <div className="space-y-2">
<h1 className="text-2xl sm:text-3xl md:text-4xl font-black font-lalezar leading-tight text-transparent bg-clip-text bg-gradient-to-r from-amber-100 via-white to-amber-300"> <h1 className="text-2xl sm:text-3xl md:text-4xl font-black font-lalezar leading-tight text-transparent bg-clip-text bg-gradient-to-r from-amber-100 via-white to-amber-300">
کاتالوگ تخصصی و راهنمای بالینی مکمل‌های دارویی کانینا کاتالوگ تخصصی و راهنمای بالینی مکمل‌های دارویی کنینا
</h1> </h1>
<p className="text-[11px] md:text-xs text-slate-300 font-medium max-w-md mx-auto leading-relaxed"> <p className="text-[11px] md:text-xs text-slate-300 font-medium max-w-md mx-auto leading-relaxed">
مرجع رسمی مکمل‌های درمانی حیوانات خانگی با استانداردهای داروسازی آلمان (GMP) و تاییدیه‌های ارتوپدی و پوست مرجع رسمی مکمل‌های درمانی حیوانات خانگی با استانداردهای داروسازی آلمان (GMP) و تاییدیه‌های ارتوپدی و پوست
@ -242,7 +242,7 @@ export default function CatalogPageSpread({
<div className="bg-amber-50/70 border border-amber-200/60 rounded-xl p-2.5 flex items-center gap-2.5 shrink-0"> <div className="bg-amber-50/70 border border-amber-200/60 rounded-xl p-2.5 flex items-center gap-2.5 shrink-0">
<Award className="w-4 h-4 text-amber-600 shrink-0" /> <Award className="w-4 h-4 text-amber-600 shrink-0" />
<p className="text-[10px] text-amber-900 font-bold leading-relaxed"> <p className="text-[10px] text-amber-900 font-bold leading-relaxed">
تمامی فرآورده‌های کانینا آلمان مطابق با مونوگراف‌های دارویی فارماکوپه اروپا (Ph. Eur.) و تحت استاندارد GMP تولید می‌شوند. تمامی فرآورده‌های کنینا آلمان مطابق با مونوگراف‌های دارویی فارماکوپه اروپا (Ph. Eur.) و تحت استاندارد GMP تولید می‌شوند.
</p> </p>
</div> </div>
@ -298,7 +298,7 @@ export default function CatalogPageSpread({
</div> </div>
<div> <div>
<span className="text-slate-400 block">واردکننده انحصاری:</span> <span className="text-slate-400 block">واردکننده انحصاری:</span>
<span className="font-black text-white">کانینا فارما ایران</span> <span className="font-black text-white">کنینا فارما ایران</span>
</div> </div>
</div> </div>

View File

@ -464,7 +464,7 @@ export default function FlipbookCatalog({ products }: FlipbookCatalogProps) {
CP CP
</div> </div>
<h3 className="text-base font-black font-lalezar text-white"> <h3 className="text-base font-black font-lalezar text-white">
کاتالوگ تخصصی کانینا آلمان کاتالوگ تخصصی کنینا آلمان
</h3> </h3>
<p className="text-[11px] text-slate-400 leading-relaxed font-medium"> <p className="text-[11px] text-slate-400 leading-relaxed font-medium">
جهت باز کردن کاتالوگ و شروع مطالعه، روی صفحه چپ یا کلید ← کیبورد را بزنید. جهت باز کردن کاتالوگ و شروع مطالعه، روی صفحه چپ یا کلید ← کیبورد را بزنید.

View File

@ -351,7 +351,7 @@ export const PRODUCTS: Product[] = [
name: "دکتر ماری کونیگ", name: "دکتر ماری کونیگ",
title: "متخصص تغذیه دامپزشکی", title: "متخصص تغذیه دامپزشکی",
image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200", image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200",
message: "روغن سالمون کانینا به روش پرس سرد تهیه شده تا تمامی ویتامین‌های حساس به حرارت حفظ شوند." message: "روغن سالمون کنینا به روش پرس سرد تهیه شده تا تمامی ویتامین‌های حساس به حرارت حفظ شوند."
}, },
image: "https://www.canina.de/media/6b/48/81/731000_Canino_Lachsoel_Lachs_Oel_1280x1280.png" image: "https://www.canina.de/media/6b/48/81/731000_Canino_Lachsoel_Lachs_Oel_1280x1280.png"
}, },
@ -512,7 +512,7 @@ export const PRODUCTS: Product[] = [
name: "دکتر مونیخ", name: "دکتر مونیخ",
title: "متخصص طب طبیعی دامپزشکی", title: "متخصص طب طبیعی دامپزشکی",
image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200", image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200",
message: "روغن سیاه دانه مصری کانینا حاوی مقادیر بالایی اسیدهای چرب چند غیراشباع است." message: "روغن سیاه دانه مصری کنینا حاوی مقادیر بالایی اسیدهای چرب چند غیراشباع است."
}, },
image: "https://www.canina.de/media/1b/83/7d/1715077272/Schwarz-Kuemmel-112503-V1-Canino-250ml_600x600.webp" image: "https://www.canina.de/media/1b/83/7d/1715077272/Schwarz-Kuemmel-112503-V1-Canino-250ml_600x600.webp"
}, },
@ -1066,7 +1066,7 @@ export const PRODUCTS: Product[] = [
name: "دکتر بکر", name: "دکتر بکر",
title: "متخصص تولیدمثل دامپزشکی", title: "متخصص تولیدمثل دامپزشکی",
image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200", image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200",
message: "شیر توله‌سگ کانینا نزدیک‌ترین ترکیب به شیر طبیعی سگ ماده را دارد." message: "شیر توله‌سگ کنینا نزدیک‌ترین ترکیب به شیر طبیعی سگ ماده را دارد."
}, },
image: "https://www.canina.de/media/6b/48/81/731000_Canino_Lachsoel_Lachs_Oel_1280x1280.png" image: "https://www.canina.de/media/6b/48/81/731000_Canino_Lachsoel_Lachs_Oel_1280x1280.png"
}, },
@ -1100,7 +1100,7 @@ export const PRODUCTS: Product[] = [
name: "دکتر کوهن", name: "دکتر کوهن",
title: "متخصص طب سنتی دامپزشکی", title: "متخصص طب سنتی دامپزشکی",
image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200", image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200",
message: "فرنی توله‌سگ کانینا انتقال نرم از شیر به غذای جامد را تضمین می‌کند." message: "فرنی توله‌سگ کنینا انتقال نرم از شیر به غذای جامد را تضمین می‌کند."
}, },
image: "https://www.canina.de/media/6b/48/81/731000_Canino_Lachsoel_Lachs_Oel_1280x1280.png" image: "https://www.canina.de/media/6b/48/81/731000_Canino_Lachsoel_Lachs_Oel_1280x1280.png"
}, },
@ -1224,7 +1224,7 @@ export const PRODUCTS: Product[] = [
name: "دکتر بکر", name: "دکتر بکر",
title: "متخصص تولیدمثل دامپزشکی", title: "متخصص تولیدمثل دامپزشکی",
image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200", image: "https://images.unsplash.com/photo-1559839734-2b71f1536783?auto=format&fit=crop&q=80&w=200",
message: "شیر گربه کانینا با تورین بالا برای سلامت قلب و بینایی بچه‌گربه‌ها حیاتی است." message: "شیر گربه کنینا با تورین بالا برای سلامت قلب و بینایی بچه‌گربه‌ها حیاتی است."
}, },
image: "https://www.canina.de/media/6b/48/81/731000_Canino_Lachsoel_Lachs_Oel_1280x1280.png" image: "https://www.canina.de/media/6b/48/81/731000_Canino_Lachsoel_Lachs_Oel_1280x1280.png"
} }

View File

@ -9,6 +9,9 @@ export interface User {
role: string; role: string;
walletBalance: number; walletBalance: number;
charityDonationTotal: number; charityDonationTotal: number;
hasPassword?: boolean;
password?: string;
currentPassword?: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
pets?: unknown[]; pets?: unknown[];

View File

@ -90,10 +90,10 @@ export class ProductService {
const priceVal = Number(data.priceValue || 0); const priceVal = Number(data.priceValue || 0);
const defaultSpecialist = local?.specialist || { const defaultSpecialist = local?.specialist || {
name: 'تیم تخصصی کانینا', name: 'تیم تخصصی کنینا',
title: 'مشاور علمی و دارویی', title: 'مشاور علمی و دارویی',
image: '/images/vets/vet1.webp', image: '/images/vets/vet1.webp',
message: 'تمامی محصولات مکمل کانینا دارای استانداردهای دارویی آلمان و تاییدیه کلینیکی هستند.' message: 'تمامی محصولات مکمل کنینا دارای استانداردهای دارویی آلمان و تاییدیه کلینیکی هستند.'
}; };
return { return {

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import { create } from "zustand"; import { create } from "zustand";
import { persist } from "zustand/middleware"; import { persist } from "zustand/middleware";
import { authService } from "../services/authService"; import { authService, User } from "../services/authService";
import api from "../services/api"; import api from "../services/api";
import { useCartStore } from "./cartStore"; import { useCartStore } from "./cartStore";
import { usePetStore } from "./usePetStore"; import { usePetStore } from "./usePetStore";
@ -37,6 +37,7 @@ interface UserProfile {
mobile: string; mobile: string;
walletBalance: number; walletBalance: number;
charityDonationTotal: number; charityDonationTotal: number;
hasPassword?: boolean;
addresses: Address[]; addresses: Address[];
transactions: Transaction[]; transactions: Transaction[];
} }
@ -49,7 +50,7 @@ interface UserStore {
setRole: (role: UserRole) => void; setRole: (role: UserRole) => void;
setLoggedIn: (isLoggedIn: boolean) => void; setLoggedIn: (isLoggedIn: boolean) => void;
setAuthModalOpen: (open: boolean) => void; setAuthModalOpen: (open: boolean) => void;
updateProfile: (profile: Partial<UserProfile>) => Promise<void>; updateProfile: (profile: Partial<UserProfile & { password?: string; currentPassword?: string }>) => Promise<void>;
addAddress: (address: Address) => Promise<void>; addAddress: (address: Address) => Promise<void>;
updateAddress: (id: string, address: Address) => Promise<void>; updateAddress: (id: string, address: Address) => Promise<void>;
deleteAddress: (id: string) => Promise<void>; deleteAddress: (id: string) => Promise<void>;
@ -72,6 +73,7 @@ export const useUserStore = create<UserStore>()(
mobile: "", mobile: "",
walletBalance: 0, walletBalance: 0,
charityDonationTotal: 0, charityDonationTotal: 0,
hasPassword: false,
addresses: [], addresses: [],
transactions: [], transactions: [],
}, },
@ -80,7 +82,7 @@ export const useUserStore = create<UserStore>()(
setAuthModalOpen: (open) => set({ isAuthModalOpen: open }), setAuthModalOpen: (open) => set({ isAuthModalOpen: open }),
updateProfile: async (updates) => { updateProfile: async (updates) => {
try { try {
const profileData = await authService.updateProfile(updates); const profileData = await authService.updateProfile(updates as Partial<User>);
set((state) => ({ set((state) => ({
profile: { profile: {
...state.profile, ...state.profile,
@ -88,6 +90,7 @@ export const useUserStore = create<UserStore>()(
lastName: profileData.lastName, lastName: profileData.lastName,
email: profileData.email, email: profileData.email,
mobile: profileData.mobile || "", mobile: profileData.mobile || "",
hasPassword: profileData.hasPassword !== undefined ? profileData.hasPassword : state.profile.hasPassword,
} }
})); }));
} catch (error) { } catch (error) {
@ -246,6 +249,7 @@ export const useUserStore = create<UserStore>()(
mobile: profileData.mobile || "", mobile: profileData.mobile || "",
walletBalance: backendWallet, walletBalance: backendWallet,
charityDonationTotal: backendCharity, charityDonationTotal: backendCharity,
hasPassword: profileData.hasPassword !== undefined ? profileData.hasPassword : state.profile.hasPassword,
addresses: Array.isArray(pd.addresses) && pd.addresses.length > 0 ? (pd.addresses as Address[]) : state.profile.addresses, addresses: Array.isArray(pd.addresses) && pd.addresses.length > 0 ? (pd.addresses as Address[]) : state.profile.addresses,
transactions: backendTransactions.length > 0 ? backendTransactions : state.profile.transactions transactions: backendTransactions.length > 0 ? backendTransactions : state.profile.transactions
} }