- Discount store pages (products, cart, orders, order detail) - Blog pages and services - Payment gateway callback page - AppImage component, EmptyState, LoadingState, PageHeader - DiscountCartService, DiscountOrderService, DiscountProductService - BlogCategoryService, BlogPostService, SitePageService, ImageCacheService - PhoneVerifyForm component - Profile Hub page - UI/UX improvements across all pages - landing.js for homepage - Config and routing updates
23 KiB
🎨 پلن جامع یکپارچهسازی UI/UX — کارا بازار سلامت
تاریخ: بهمن ۱۴۰۴
وضعیت: ✅ تمام ۶ فاز + فاز ۷ (ناوبری و امنیت) تکمیل شد
هدف: یکپارچهسازی طراحی FrontOffice با ۲۰٪ تغییر UX و ۵۰٪ تغییر UI
فریمورک: Blazor Server + MudBlazor 8.14
📋 تاریخچه تغییرات جزئی: CHANGELOG.md
📊 خلاصه اجرایی
| شاخص | وضعیت قبل | وضعیت بعد |
|---|---|---|
| صفحات کل | ۴۱ صفحه + ۶ کامپوننت مشترک | ۴۱ صفحه + ۸ کامپوننت مشترک |
| الگوی PageHeader | ۱۹ صفحه از ۴۱ | ۳۲+ صفحه ✅ |
| Inline Style سنگین | ۱۱ صفحه 🔴 | ≤۲ صفحه (فقط gradientهای تزئینی) ✅ |
| Loading State | ۲ الگوی متفاوت (Circular vs Linear) | <LoadingState> واحد ✅ |
| Empty State | ۴+ الگوی متناقض | <EmptyState> واحد ✅ |
| Container Spacing | ۶ الگوی متناقض | py-6 استاندارد ✅ |
| رنگبندی | رنگهای hardcoded در ۸+ صفحه | CSS Variable ✅ |
| تم پالت | Primary #0380C0 (آبی ساده) |
Indigo #6366f1 + Full PaletteDark ✅ |
| Elevation | مخلوط ۲/۳/۴ | استاندارد ۰–۲ ✅ |
| Build | ۰ خطا | ۰ خطا ✅ |
فازها
| فاز | عنوان | وضعیت |
|---|---|---|
| ۱ | زیرساخت دیزاین سیستم | ✅ تکمیل |
| ۲ | صفحات پروفایل | ✅ تکمیل |
| ۳ | صفحات تخصصی | ✅ تکمیل |
| ۴ | بهبود بصری فروشگاه | ✅ تکمیل |
| ۵ | صفحات عمومی | ✅ تکمیل |
| ۶ | پالیش و تست | ✅ تکمیل |
| ۷ | ناوبری، امنیت و بازسازی کامپوننتها | ✅ تکمیل |
🔍 بخش ۱: تحلیل ضعفهای جاری
۱.۱ ناسازگاریهای ساختاری (Structural)
❌ ۱.۱.۱ — PageHeader دوگانه
مشکل: نیمی از صفحات از <PageHeader> استفاده میکنند، نیم دیگر header دستی دارند.
از <PageHeader> استفاده میکنند ✅ |
Header دستی دارند ❌ |
|---|---|
| Store/* (۸ صفحه) | Profile/* (۷ صفحه) |
| DiscountStore/* (۶ صفحه) | Club/* (۲ صفحه) |
| Gateway/* (۳ صفحه) | Commission/* (۲ صفحه) |
| Package/* (۲ صفحه) | Network/* (۱ صفحه) |
| PackageDetail, Checkout | Blog/*, Index, About, Contact, FAQ |
تأثیر: ظاهر متفاوت دکمه بازگشت، فاصلهبندی ناهماهنگ
❌ ۱.۱.۲ — Container MaxWidth متناقض
MaxWidth.Large → اکثر صفحات
MaxWidth.Medium → Personal, Settings, OrderTracking, Blog/Post, Gateway/*
MaxWidth.Small → ChangePassword
ترکیبی (loading≠content) → OrderDetail, ProductDetail, PackageDetail
تأثیر: برخی صفحات پهنتر از حد نیاز هستند (مثلاً فرمهای ساده با Large)
❌ ۱.۱.۳ — Container Padding متناقض
py-6 → اکثریت (استاندارد)
py-8 → Package/Packages, Package/MyPackages
pa-2 pa-md-6 → Store/Products, DiscountStore/Products
py-6 py-md-10 → Gateway/*
py-4 py-md-6 → DiscountStore/ProductDetail
py-16 سکشنی → Index, About, Contact, FAQ
قاعده پیشنهادی: py-6 برای صفحات داخلی، section-based برای صفحات عمومی
۱.۲ ناسازگاریهای بصری (Visual)
❌ ۱.۲.۱ — Inline Style سنگین (۱۱ صفحه)
| صفحه | نمونه مشکلدار |
|---|---|
| Index.razor | Style="color:#fff; font-size:clamp(1.6rem,4.5vw,2.4rem);" |
| Profile/Index | Style="background:rgba(99,102,241,.12);" |
| Store/ProductDetail | style="width:100%;height:360px;background-image:url(...);" |
| PackageDetail | Style="background: radial-gradient(600px 280px..." |
| Checkout | Style="background: radial-gradient(..." + Elevation="4" |
| Blog/Index | Style="color:#fff; font-weight:700;", Style="font-size:3.5rem;" |
| Blog/Post | Style="width:100%; height:100%;", Style="font-size:4rem;" |
| About | Style="background: radial-gradient(...);" |
| WeeklyBalance | Style="background: linear-gradient(135deg, #e8f5e9..." |
| Gateway/** | Style="background:rgba(16,185,129,.12)...", Style="color:#10b981;" |
| DiscountStore/ProductDetail | Style="background:rgba(16,185,129,.06)..." |
❌ ۱.۲.۲ — رنگهای Hardcoded
| رنگ | استفاده | باید باشد |
|---|---|---|
#10b981 |
Gateway (سبز فروشگاه) | var(--ds-color-store) |
#ef4444 |
Gateway (قرمز تخفیفی) | var(--ds-color-discount) |
#6366f1, #818cf8, #a78bfa |
Hero/Banner gradients | var(--ds-gradient-primary) |
rgba(99,102,241,.12) |
Dashboard backgrounds | var(--ds-primary-soft) |
rgba(16,185,129,.06) |
Discount product highlights | var(--ds-success-soft) |
#e8f5e9, #c8e6c9 |
WeeklyBalance stat cards | var(--ds-success-gradient) |
❌ ۱.۲.۳ — Elevation ناهماهنگ
Elevation="0" → Blog cards (با border)
Elevation="1" → برخی صفحات
Elevation="2" → اکثر صفحات (استاندارد)
Elevation="3" → WeeklyBalance stat cards
Elevation="4" → Checkout.razor
قاعده پیشنهادی: Elevation="0" با border = کارتهای اطلاعاتی، Elevation="2" = default
❌ ۱.۲.۴ — Paper Rounding ناهماهنگ
rounded-lg (16px) → اکثریت
rounded-xl (20px) → Index, Profile/Index, Gateway, DiscountStore/ProductDetail
بدون class → design system !important → 12px
۱.۳ ناسازگاریهای UX (تجربه کاربری)
❌ ۱.۳.۱ — Loading State دوگانه
MudProgressCircular → Store/*, DiscountStore/*, Package/*, About, Blog, Addresses
MudProgressLinear → Club/*, Commission/*, Network/*, RegisterWizard
مشکل: کاربر دو تجربه مختلف «در حال بارگذاری» میبیند
❌ ۱.۳.۲ — Empty State ناهماهنگ
MudAlert Severity.Info → Store/Orders, Categories, DiscountStore/Orders
Icon + Text + Button → Addresses, Package/MyPackages, PackageDetail
MudAlert Severity.Warning → WithdrawalRequests
Custom dashed-border paper → Blog/Index
❌ ۱.۳.۳ — Routing Directive ناهماهنگ
@attribute [Route(RouteConstants...)] → ۳۹ صفحه ✅
@page "/categories" → Categories.razor ❌
@page "/blog/{Slug}" → Blog/Post.razor ❌
❌ ۱.۳.۴ — PackageDetail Loading/Error بدون Container
Loading و Error state در PackageDetail.razor بدون MudContainer رندر میشوند → محتوا تمامعرض نمایش مییابد.
❌ ۱.۳.۵ — تم فعلی کمرنگ
// CustomMudTheme.cs فعلی
Primary = "#0380C0" // آبی ساده
// بدون Secondary، Tertiary، Info، Warning تعریفشده
// بدون PaletteDark
// بدون LayoutProperties
مشکلات:
- فقط Primary تعریف شده، بقیه رنگها default MudBlazor
- Dark mode بدون palette اختصاصی
- بدون
DefaultBorderRadius،AppbarHeightو غیره - تناقض بین Primary
#0380C0و gradientهای CSS با#6366f1
🎯 بخش ۲: معماری دیزاین سیستم هدف
۲.۱ سلسلهمراتب صفحات
┌─────────────────────────────────────────────┐
│ MainLayout │
│ ├─ AppBar (fixed, transparent) │
│ ├─ MudMainContent │
│ │ ├─ [Public Pages] → Section-based │
│ │ │ (Index, About, Contact, FAQ, Blog) │
│ │ └─ [Internal Pages] → Container-based │
│ │ ├─ <PageHeader/> │
│ │ ├─ Content (MudStack/MudGrid) │
│ │ └─ </MudContainer> │
│ ├─ Footer (hidden on mobile) │
│ └─ BottomNav (mobile only) │
└─────────────────────────────────────────────┘
۲.۲ قواعد واحد (Single Source of Truth)
| قاعده | مقدار |
|---|---|
| Container MaxWidth | Large = لیست/گرید, Medium = فرم/جزئیات, Small = تکفرم ساده |
| Container Spacing | py-6 صفحات داخلی, section-based صفحات عمومی |
| Paper Elevation | 0 با border = کارت اطلاعاتی, 2 = default |
| Paper Rounding | rounded-lg = default, rounded-xl = hero/banner |
| Loading State | <LoadingState/> component واحد |
| Empty State | <EmptyState/> component واحد |
| Page Header | <PageHeader/> در تمام صفحات داخلی |
| Content Wrapper | MudStack Spacing="3" بعد از PageHeader |
۲.۳ CSS Variables هدف
:root {
/* ── Brand Colors ── */
--ds-brand-primary: #6366f1; /* Indigo — هویت اصلی */
--ds-brand-secondary: #8b5cf6; /* Purple */
--ds-brand-accent: #a78bfa; /* Light purple */
/* ── Semantic Colors ── */
--ds-color-store: #10b981; /* فروشگاه عادی */
--ds-color-discount: #ef4444; /* فروشگاه تخفیفی */
--ds-color-success: #10b981;
--ds-color-warning: #f59e0b;
--ds-color-error: #ef4444;
--ds-color-info: #3b82f6;
/* ── Soft Backgrounds ── */
--ds-primary-soft: rgba(99,102,241,.08);
--ds-success-soft: rgba(16,185,129,.08);
--ds-error-soft: rgba(239,68,68,.08);
--ds-warning-soft: rgba(245,158,11,.08);
/* ── Gradients ── */
--ds-gradient-primary: linear-gradient(135deg, #6366f1 0%, #818cf8 50%, #a78bfa 100%);
--ds-gradient-store: linear-gradient(135deg, #10b981 0%, #34d399 100%);
--ds-gradient-discount: linear-gradient(135deg, #ef4444 0%, #f97316 50%, #f59e0b 100%);
--ds-gradient-success: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
/* ── Spacing (existing) ── */
--ds-radius-sm: 8px;
--ds-radius-md: 12px;
--ds-radius-lg: 16px;
--ds-radius-xl: 20px;
--ds-transition: 0.2s ease;
--ds-shadow-sm: 0 1px 3px rgba(0,0,0,.06);
--ds-shadow-md: 0 4px 12px rgba(0,0,0,.08);
--ds-shadow-lg: 0 8px 24px rgba(0,0,0,.10);
}
🚀 بخش ۳: فازبندی اجرا
🔷 فاز ۱ — زیرساخت دیزاین سیستم (UI ~15%)
اولویت: بالا | ریسک: پایین | حجم: ۶ فایل
| # | تسک | فایل | نوع تغییر |
|---|---|---|---|
| 1.1 | ارتقاء CustomMudTheme.cs — اضافه کردن PaletteDark، LayoutProperties، رنگهای Secondary/Tertiary/Info، تغییر Primary به #6366f1 |
CustomMudTheme.cs |
UI |
| 1.2 | توسعه CSS Variables — اضافه کردن brand colors، semantic colors، soft backgrounds، gradients | site.css |
UI |
| 1.3 | ساخت <LoadingState> component واحد |
Shared/LoadingState.razor (جدید) |
UX |
| 1.4 | ساخت <EmptyState> component واحد |
Shared/EmptyState.razor (جدید) |
UX |
| 1.5 | بهبود <PageHeader> — اضافه کردن آیکون، subtitle اختیاری |
Shared/PageHeader.razor |
UI |
| 1.6 | اضافه کردن .page-container CSS pattern |
site.css |
UI |
🔷 فاز ۲ — یکپارچهسازی صفحات Profile (UI ~10%, UX ~5%)
اولویت: بالا | ریسک: پایین | حجم: ۹ فایل
| # | تسک | تغییرات |
|---|---|---|
| 2.1 | Profile/Personal → جایگزینی header دستی با <PageHeader> |
UX |
| 2.2 | Profile/Addresses → <PageHeader> + <LoadingState> + <EmptyState> |
UX |
| 2.3 | Profile/Wallet → <PageHeader> |
UX |
| 2.4 | Profile/Settings → <PageHeader> |
UX |
| 2.5 | Profile/Tree → <PageHeader> |
UX |
| 2.6 | Profile/WithdrawalRequests → <PageHeader> + <EmptyState> |
UX |
| 2.7 | Profile/ChangePassword → <PageHeader> |
UX |
| 2.8 | Profile/Index (Dashboard) → حذف inline styles، استفاده از CSS Variables |
UI |
| 2.9 | Club/MembershipPage + Club/FeaturesPage → <PageHeader> + <LoadingState> |
UX |
🔷 فاز ۳ — یکپارچهسازی صفحات تخصصی (UI ~5%, UX ~5%)
اولویت: متوسط | ریسک: پایین | حجم: ۵ فایل
| # | تسک | تغییرات |
|---|---|---|
| 3.1 | Commission/Dashboard → <PageHeader> + <LoadingState> |
UX |
| 3.2 | Commission/WeeklyBalance → <PageHeader> + حذف inline gradient styles |
UI + UX |
| 3.3 | Network/NetworkStatistics → <PageHeader> + <LoadingState> |
UX |
| 3.4 | PackageDetail → wrap loading/error در MudContainer |
UX bug fix |
| 3.5 | Checkout → Elevation=4→2، حذف inline radial-gradient |
UI |
🔷 فاز ۴ — بهبود بصری فروشگاهها (UI ~10%)
اولویت: متوسط | ریسک: پایین | حجم: ۶ فایل
| # | تسک | تغییرات |
|---|---|---|
| 4.1 | Store/Products → حذف inline hero styles، استفاده از CSS class |
UI |
| 4.2 | Store/ProductDetail → حذف inline image styles، ساخت .product-image-main CSS |
UI |
| 4.3 | DiscountStore/ProductDetail → حذف hardcoded rgba، استفاده از --ds-success-soft |
UI |
| 4.4 | Gateway/* (۳ صفحه) → حذف hardcoded #10b981/#ef4444، استفاده از --ds-color-store/--ds-color-discount |
UI |
| 4.5 | یکسانسازی Elevation → 0 با border یا 2 |
UI |
| 4.6 | یکسانسازی Container spacing → py-6 |
UI |
🔷 فاز ۵ — بهبود صفحات عمومی و بلاگ (UI ~10%)
اولویت: پایین | ریسک: پایین | حجم: ۷ فایل
| # | تسک | تغییرات |
|---|---|---|
| 5.1 | Index.razor → حذف inline styles از hero، استفاده از CSS class |
UI |
| 5.2 | About.razor → حذف inline radial-gradient |
UI |
| 5.3 | Contact.razor → cleanup minor inline styles |
UI |
| 5.4 | FAQ.razor → cleanup minor inline styles |
UI |
| 5.5 | Blog/Index → حذف inline hero styles، استفاده از CSS class |
UI |
| 5.6 | Blog/Post → حذف inline styles از hero image و typography |
UI |
| 5.7 | RegisterWizard → بهینهسازی wizard-section dark mode |
UI |
🔷 فاز ۶ — Polish نهایی و فرآیندی (UX ~10%)
اولویت: پایین | ریسک: بسیار پایین | حجم: ۴ فایل + تست
| # | تسک | تغییرات |
|---|---|---|
| 6.1 | Fix routing inconsistency — Categories + Blog/Post | UX |
| 6.2 | MudSnackbar notifications styling | UI |
| 6.3 | Dialog styling consistency (AuthDialog, AddressDialogs) | UI |
| 6.4 | Micro-interactions — button press, card hover, page transition | UI |
| 6.5 | تست کامل Dark Mode در تمام صفحات | UI + QA |
| 6.6 | تست Mobile Responsive در تمام صفحات | UX + QA |
📈 بخش ۴: جدول تأثیرگذاری
تأثیر UI (هدف ~۵۰٪ تغییر)
| حوزه | تعداد فایل | درصد تأثیر |
|---|---|---|
| Theme + CSS Variables | ۲ | ۱۵% (تأثیر سراسری) |
| حذف Inline Styles | ۱۱ | ۱۵% |
| یکسانسازی Elevation/Rounding | ۲۰+ | ۱۰% |
| بهبود رنگبندی (CSS Variables) | ۸ | ۵% |
| Micro-interactions | سراسری | ۵% |
| جمع | ~۵۰% |
تأثیر UX (هدف ~۲۰٪ تغییر)
| حوزه | تعداد فایل | درصد تأثیر |
|---|---|---|
| PageHeader یکپارچه | ۱۲ صفحه | ۸% |
| LoadingState واحد | ۱۵+ صفحه | ۴% |
| EmptyState واحد | ۸+ صفحه | ۳% |
| Container fixes (PackageDetail) | ۲ | ۲% |
| Routing consistency | ۲ | ۱% |
| Flow improvements | ۲ | ۲% |
| جمع | ~۲۰% |
🔧 بخش ۵: مشخصات فنی کامپوننتهای جدید
۵.۱ LoadingState Component
@* Shared/LoadingState.razor *@
<MudStack AlignItems="AlignItems.Center" Class="py-16">
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Large" />
@if (!string.IsNullOrWhiteSpace(Message))
{
<MudText Typo="Typo.body1" Class="mud-text-secondary mt-2">@Message</MudText>
}
</MudStack>
@code {
[Parameter] public string Message { get; set; } = "در حال بارگذاری...";
}
۵.۲ EmptyState Component
@* Shared/EmptyState.razor *@
<MudStack AlignItems="AlignItems.Center" Class="py-12" Spacing="3">
<MudIcon Icon="@Icon" Size="Size.Large" Color="Color.Default" Class="mud-text-disabled" />
<MudText Typo="Typo.h6" Class="mud-text-secondary">@Title</MudText>
@if (!string.IsNullOrWhiteSpace(Description))
{
<MudText Typo="Typo.body2" Class="mud-text-secondary" Style="max-width:400px; text-align:center;">
@Description
</MudText>
}
@if (!string.IsNullOrWhiteSpace(ActionText))
{
<MudButton Variant="Variant.Filled" Color="Color.Primary"
Href="@ActionHref" OnClick="@OnAction" Class="mt-2">
@ActionText
</MudButton>
}
</MudStack>
@code {
[Parameter] public string Icon { get; set; } = Icons.Material.Filled.Inbox;
[Parameter] public string Title { get; set; } = "موردی یافت نشد";
[Parameter] public string? Description { get; set; }
[Parameter] public string? ActionText { get; set; }
[Parameter] public string? ActionHref { get; set; }
[Parameter] public EventCallback OnAction { get; set; }
}
۵.۳ PageHeader ارتقاءیافته
@* Shared/PageHeader.razor — ارتقاءیافته *@
<div class="page-header">
<MudStack Spacing="0">
<MudText Typo="Typo.h5">@Title</MudText>
@if (!string.IsNullOrWhiteSpace(Subtitle))
{
<MudText Typo="Typo.body2" Class="mud-text-secondary">@Subtitle</MudText>
}
</MudStack>
@if (!string.IsNullOrWhiteSpace(BackHref))
{
<MudButton Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack"
Href="@BackHref">بازگشت</MudButton>
}
else
{
<MudButton Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack"
OnClick="GoBack">بازگشت</MudButton>
}
</div>
@code {
[Parameter] public string Title { get; set; } = "";
[Parameter] public string? Subtitle { get; set; }
[Parameter] public string? BackHref { get; set; }
[Inject] private IJSRuntime JS { get; set; } = default!;
private async Task GoBack() => await JS.InvokeVoidAsync("history.back");
}
✅ بخش ۶: چکلیست تکمیل هر فاز
فاز ۱ چکلیست:
CustomMudTheme.cs— Primary→#6366f1, PaletteDark اضافه شدsite.css— CSS Variables جدید (brand, semantic, soft, gradient)Shared/LoadingState.razor— ساخته و تست شدShared/EmptyState.razor— ساخته و تست شدShared/PageHeader.razor— Subtitle parameter اضافه شد.page-containerCSS pattern اضافه شد- Build: 0 errors ✅
- Dark Mode: صحیح ✅
- Mobile: صحیح ✅
فاز ۲ چکلیست:
Profile/Personal→<PageHeader>Profile/Addresses→<PageHeader>+<LoadingState>+<EmptyState>Profile/Wallet→<PageHeader>Profile/Settings→<PageHeader>Profile/Tree→<PageHeader>Profile/WithdrawalRequests→<PageHeader>+<EmptyState>Profile/ChangePassword→<PageHeader>Profile/Index→ inline styles → CSSClub/*→<PageHeader>+<LoadingState>- Build: 0 errors ✅
فاز ۳ چکلیست:
Commission/*→<PageHeader>+<LoadingState>Network/*→<PageHeader>+<LoadingState>PackageDetail→ MudContainer wrapper برای loading/errorCheckout→ Elevation fix + inline cleanupWeeklyBalance→ inline gradient → CSS class- Build: 0 errors ✅
فاز ۴ چکلیست:
Store/Products→ hero inline → CSSStore/ProductDetail→ image inline → CSS classDiscountStore/ProductDetail→ rgba → variableGateway/*→ hardcoded → variable- Elevation یکسانسازی
- Container spacing یکسانسازی
- Build: 0 errors ✅
فاز ۵ چکلیست:
Index.razor→ hero inline cleanupAbout.razor→ radial-gradient cleanupBlog/Index+Blog/Post→ inline cleanupContact.razor+FAQ.razor→ minor cleanup- Build: 0 errors ✅
فاز ۶ چکلیست:
- Routing fix (Categories, Blog/Post)
- MudSnackbar styling
- Dialog consistency
- Dark mode full test
- Mobile responsive full test
- Build: 0 errors ✅
📋 بخش ۷: خلاصه تغییرات در یک نگاه
فایلهای تغییریافته:
├── CustomMudTheme.cs [فاز ۱] — ارتقاء کامل تم
├── site.css [فاز ۱-۵] — CSS Variables + classهای جدید
├── Shared/LoadingState.razor [فاز ۱] — جدید
├── Shared/EmptyState.razor [فاز ۱] — جدید
├── Shared/PageHeader.razor [فاز ۱] — بهبود
├── Profile/* (۷ فایل) [فاز ۲] — PageHeader + LoadingState
├── Profile/Index.razor [فاز ۲] — حذف inline styles
├── Club/* (۲ فایل) [فاز ۲] — PageHeader + LoadingState
├── Commission/* (۲ فایل) [فاز ۳] — PageHeader + LoadingState + cleanup
├── Network/* (۱ فایل) [فاز ۳] — PageHeader + LoadingState
├── PackageDetail.razor [فاز ۳] — Container fix
├── Checkout.razor [فاز ۳] — Elevation + cleanup
├── Store/* (۲ فایل) [فاز ۴] — inline → CSS
├── DiscountStore/ProductDetail [فاز ۴] — rgba → variable
├── Gateway/* (۳ فایل) [فاز ۴] — hardcoded → variable
├── Index.razor [فاز ۵] — hero cleanup
├── About.razor [فاز ۵] — gradient cleanup
├── Blog/* (۲ فایل) [فاز ۵] — inline cleanup
├── Contact.razor + FAQ.razor [فاز ۵] — minor cleanup
└── Categories + Blog/Post [فاز ۶] — routing fix
مجموع فایلهای تأثیرپذیر: ~۳۵ فایل
فایلهای جدید: ۲ (LoadingState, EmptyState)
میزان تغییر UI: ~۵۰٪
میزان تغییر UX: ~۲۰٪
ریسک شکست: پایین (تغییرات تدریجی، build verification در هر فاز)