1419 lines
60 KiB
Markdown
1419 lines
60 KiB
Markdown
# 🎨 طرح نوسازی رابط کاربری فرانتآفیس + سیستم بلاگ
|
||
|
||
> **تاریخ شروع:** تیر ۱۴۰۴
|
||
> **آخرین بروزرسانی:** بهمن ۱۴۰۴
|
||
> **وضعیت:** فازهای ۱ تا ۶ تکمیل ✅ — فاز ۷ در حال اجرا
|
||
> **اولویت:** بالا
|
||
|
||
---
|
||
|
||
## فهرست مطالب
|
||
|
||
1. [خلاصه اجرایی](#1-خلاصه-اجرایی)
|
||
2. [وضعیت فعلی](#2-وضعیت-فعلی)
|
||
3. [اهداف پروژه](#3-اهداف-پروژه)
|
||
4. [معماری فنی](#4-معماری-فنی)
|
||
5. [فاز ۱ — موجودیتهای بکند (CMS)](#5-فاز-۱--موجودیتهای-بکند-cms)
|
||
6. [فاز ۲ — پنل مدیریت بلاگ (BackOffice)](#6-فاز-۲--پنل-مدیریت-بلاگ-backoffice)
|
||
7. [فاز ۳ — صفحات محتوای دینامیک (درباره ما / تماس با ما)](#7-فاز-۳--صفحات-محتوای-دینامیک-درباره-ما--تماس-با-ما)
|
||
8. [فاز ۴ — نوسازی لندینگ پیج](#8-فاز-۴--نوسازی-لندینگ-پیج)
|
||
9. [فاز ۵ — صفحات بلاگ فرانتآفیس](#9-فاز-۵--صفحات-بلاگ-فرانتآفیس)
|
||
10. [فاز ۶ — بهبود داشبورد کاربر](#10-فاز-۶--بهبود-داشبورد-کاربر)
|
||
11. [فاز ۷ — بهینهسازی موبایل](#11-فاز-۷--بهینهسازی-موبایل)
|
||
12. [فایلهای تغییریافته (نقشه فایلها)](#12-فایلهای-تغییریافته-نقشه-فایلها)
|
||
13. [جدول زمانی](#13-جدول-زمانی)
|
||
14. [ریسکها و وابستگیها](#14-ریسکها-و-وابستگیها)
|
||
|
||
---
|
||
|
||
## 1. خلاصه اجرایی
|
||
|
||
این سند طرح جامع نوسازی رابط کاربری **فرانتآفیس** (سمت مشتری) و ایجاد **سیستم بلاگ/مدیریت محتوا** را شامل میشود. اهداف اصلی:
|
||
|
||
- ✅ ایجاد **سیستم بلاگ** با قابلیت انتشار مقالات، مجوزها، گواهینامهها با تصاویر
|
||
- ✅ **صفحات دینامیک** درباره ما و تماس با ما (قابل مدیریت از پنل ادمین)
|
||
- ✅ **نوسازی لندینگ پیج** با طراحی حرفهای و حس زنده بودن سامانه
|
||
- ✅ **صفحات بلاگ فرانتآفیس** — لیست مقالات با جستجو/فیلتر + صفحه جزئیات مقاله
|
||
- ✅ **بهبود داشبورد** کاربر با طراحی مدرن و اطلاعات پویا
|
||
- 🔄 **طراحی Mobile-First** (اولویت موبایل) — فاز ۷
|
||
- ⛔ بدون تغییر UI سمت ادمین (فقط افزودن صفحات مدیریت بلاگ/محتوا)
|
||
|
||
---
|
||
|
||
## 2. وضعیت فعلی
|
||
|
||
### 2.1 پشته فنی
|
||
|
||
| لایه | تکنولوژی |
|
||
|------|----------|
|
||
| FrontOffice | Blazor Server (.NET 9) + MudBlazor 8.14.0 |
|
||
| BackOffice | Blazor WebAssembly (.NET 9) + MudBlazor 8.14.0 |
|
||
| CMS (Backend) | .NET 9 gRPC + MediatR CQRS + EF Core |
|
||
| دیتابیس | SQL Server |
|
||
| فایلها | FMS (File Management Service) via gRPC |
|
||
| زبان UI | فارسی (RTL) + فونت Vazir |
|
||
| تم | Light/Dark toggle |
|
||
|
||
### 2.2 صفحات فعلی فرانتآفیس
|
||
|
||
| صفحه | مسیر | وضعیت |
|
||
|------|------|-------|
|
||
| لندینگ | `/` | ✅ Hero گرادیان + Features + Timeline + Stats + بلاگ + Testimonials + FAQ + CTA |
|
||
| درباره ما | `/about` | ✅ محتوای دینامیک از CMS (با fallback) |
|
||
| تماس با ما | `/contact` | ✅ محتوای دینامیک از CMS + فرم تماس |
|
||
| بلاگ | `/blog` | ✅ لیست مقالات + جستجو + فیلتر دستهبندی + صفحهبندی |
|
||
| مقاله | `/blog/{slug}` | ✅ صفحه جزئیات مقاله + breadcrumb + تگها + شمارنده بازدید |
|
||
| سوالات متداول | `/faq` | ✅ فعال |
|
||
| پروفایل | `/profile/*` | ✅ ۱۰ صفحه (هدر گرادیانی + والتکارتها + تایلهای مدرن) |
|
||
| فروشگاه | `/products/*` | ✅ ۸ صفحه (محصولات، سبد، سفارشات، ...) |
|
||
| باشگاه | `/club/*` | ✅ ۲ صفحه |
|
||
| کمیسیون | `/commission/*` | ✅ ۳ صفحه |
|
||
| شبکه | `/network/*` | ✅ ۲ صفحه |
|
||
| پکیج | `/packages/*` | ✅ ۴ صفحه |
|
||
|
||
### 2.3 مشکلات فعلی (رفع شده ✅)
|
||
|
||
1. ~~**احساس ایستا بودن** — لندینگ پیج بدون انیمیشن و محتوای پویا~~ → ✅ فاز ۴ حل شد
|
||
2. ~~**بدون بلاگ** — امکان انتشار مقاله/خبر/مجوز وجود ندارد~~ → ✅ فازهای ۱، ۲، ۵ حل شد
|
||
3. ~~**صفحات استاتیک** — درباره ما و تماس با ما هاردکد هستند~~ → ✅ فاز ۳ حل شد
|
||
4. ~~**طراحی ساده** — فاقد عناصر بصری جذاب~~ → ✅ فازهای ۴ و ۶ حل شد
|
||
5. **موبایلمحور نبودن** — ریسپانسیو هست اما اولویت با دسکتاپ → 🔄 فاز ۷
|
||
|
||
---
|
||
|
||
## 3. اهداف پروژه
|
||
|
||
### 3.1 اهداف عملیاتی
|
||
|
||
| هدف | شاخص موفقیت |
|
||
|-----|-------------|
|
||
| سیستم بلاگ | ادمین بتواند مقاله بنویسد، دستهبندی کند، تصویر آپلود کند، منتشر/آرشیو کند |
|
||
| محتوای دینامیک | ادمین بتواند درباره ما و تماس با ما را از پنل ویرایش کند |
|
||
| لندینگ حرفهای | اولین بازدید حس «پلتفرم زنده و فعال» بدهد |
|
||
| Mobile-First | تمام صفحات در موبایل عالی نمایش داده شوند |
|
||
| عملکرد | زمان بارگذاری صفحه اول < ۳ ثانیه |
|
||
|
||
### 3.2 محدوده (Scope)
|
||
|
||
```
|
||
✅ در محدوده:
|
||
- سیستم بلاگ کامل (CMS → BackOffice → FrontOffice)
|
||
- صفحات محتوای دینامیک (About, Contact)
|
||
- نوسازی لندینگ پیج
|
||
- بهبود داشبورد/پروفایل
|
||
- بهینهسازی موبایل
|
||
- افزودن صفحات مدیریت بلاگ به BackOffice
|
||
|
||
⛔ خارج از محدوده:
|
||
- تغییرات UI سایر بخشهای BackOffice
|
||
- سیستم کامنتگذاری (فاز بعدی)
|
||
- سیستم جستجوی پیشرفته (فاز بعدی)
|
||
- اپلیکیشن موبایل (PWA یا Native)
|
||
```
|
||
|
||
---
|
||
|
||
## 4. معماری فنی
|
||
|
||
### 4.1 جریان داده بلاگ
|
||
|
||
```
|
||
┌──────────────┐ gRPC ┌───────────┐ EF Core ┌──────────┐
|
||
│ BackOffice │ ──────────► │ CMS │ ────────────► │ SQL Server│
|
||
│ (Admin CRUD) │ ◄────────── │ (gRPC) │ ◄──────────── │ │
|
||
└──────────────┘ └───────────┘ └──────────┘
|
||
│ ▲
|
||
gRPC │ │
|
||
▼ │
|
||
┌──────────────┐
|
||
│ FrontOffice │
|
||
│ (مشاهده بلاگ)│
|
||
└──────────────┘
|
||
│
|
||
FMS │ (آپلود تصاویر)
|
||
▼
|
||
┌──────────┐
|
||
│ FMS │
|
||
│ (فایلها) │
|
||
└──────────┘
|
||
```
|
||
|
||
### 4.2 موجودیتهای جدید (Entity Relationship)
|
||
|
||
```
|
||
BlogPost (1) ──────── (N) BlogPostCategory ──────── (1) BlogCategory
|
||
│
|
||
│ (1:N)
|
||
▼
|
||
BlogPostImage
|
||
|
||
BlogPost.Tags → از Tag موجود استفاده میشود (BlogPostTag join table)
|
||
|
||
SitePage → صفحات دینامیک (About, Contact, Custom)
|
||
│
|
||
│ (1:N)
|
||
▼
|
||
SitePageSection → بخشهای هر صفحه
|
||
```
|
||
|
||
### 4.3 الگوی CQRS موجود (برای مرجع)
|
||
|
||
```
|
||
Proto (gRPC Definition)
|
||
↓
|
||
WebApi/Services/{Feature}Service.cs (extends generated gRPC base)
|
||
↓ Mapster (Proto → Command/Query)
|
||
Application/{Feature}CQ/Commands/ or Queries/
|
||
↓ MediatR
|
||
Handler → Repository (DbContext)
|
||
↓
|
||
Domain/Entities/{Entity}.cs
|
||
```
|
||
|
||
---
|
||
|
||
## 5. فاز ۱ — موجودیتهای بکند (CMS) ✅ تکمیل شد
|
||
|
||
> **وضعیت:** ✅ تکمیل | **اولویت:** بالا | **پیشنیاز:** ندارد
|
||
|
||
### 5.1 موجودیتهای جدید Domain
|
||
|
||
#### `BlogPost.cs`
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Domain/Entities/Blog/BlogPost.cs
|
||
Base: BaseAuditableEntity
|
||
|
||
Properties:
|
||
- string Title (required, max 200)
|
||
- string Slug (required, unique, max 200)
|
||
- string Summary (max 500) — خلاصه برای کارتها
|
||
- string HtmlContent (required) — محتوای HTML کامل
|
||
- string? FeaturedImagePath — تصویر شاخص
|
||
- string? FeaturedImageThumbnailPath — تامبنیل تصویر شاخص
|
||
- BlogPostStatus Status (enum: Draft, Published, Scheduled, Archived)
|
||
- DateTime? PublishedAt — زمان انتشار
|
||
- DateTime? ScheduledPublishAt — زمانبندی انتشار
|
||
- int ViewCount — تعداد بازدید
|
||
- long AuthorUserId — نویسنده
|
||
- bool IsFeatured — نمایش در صفحه اول
|
||
- int SortOrder — ترتیب نمایش
|
||
|
||
Relations:
|
||
- ICollection<BlogPostCategory> BlogPostCategories
|
||
- ICollection<BlogPostTag> BlogPostTags
|
||
- ICollection<BlogPostImage> BlogPostImages
|
||
```
|
||
|
||
#### `BlogCategory.cs`
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Domain/Entities/Blog/BlogCategory.cs
|
||
Base: BaseAuditableEntity
|
||
|
||
Properties:
|
||
- string Title (required, max 100)
|
||
- string Slug (required, unique, max 100)
|
||
- string? Description (max 500)
|
||
- string? IconName — آیکون Material
|
||
- int SortOrder
|
||
- bool IsActive
|
||
|
||
Relations:
|
||
- ICollection<BlogPostCategory> BlogPostCategories
|
||
```
|
||
|
||
#### `BlogPostCategory.cs` (Join Table)
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Domain/Entities/Blog/BlogPostCategory.cs
|
||
Base: BaseEntity
|
||
|
||
Properties:
|
||
- long BlogPostId
|
||
- long BlogCategoryId
|
||
|
||
Relations:
|
||
- BlogPost BlogPost
|
||
- BlogCategory BlogCategory
|
||
```
|
||
|
||
#### `BlogPostTag.cs` (Join Table)
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Domain/Entities/Blog/BlogPostTag.cs
|
||
Base: BaseEntity
|
||
|
||
Properties:
|
||
- long BlogPostId
|
||
- long TagId
|
||
|
||
Relations:
|
||
- BlogPost BlogPost
|
||
- Tag Tag (موجود — استفاده مجدد)
|
||
```
|
||
|
||
#### `BlogPostImage.cs`
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Domain/Entities/Blog/BlogPostImage.cs
|
||
Base: BaseAuditableEntity
|
||
|
||
Properties:
|
||
- long BlogPostId
|
||
- string ImagePath
|
||
- string ThumbnailPath
|
||
- string? AltText (max 200)
|
||
- string? Caption (max 300)
|
||
- int SortOrder
|
||
|
||
Relations:
|
||
- BlogPost BlogPost
|
||
```
|
||
|
||
#### `SitePage.cs` (صفحات دینامیک)
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Domain/Entities/Content/SitePage.cs
|
||
Base: BaseAuditableEntity
|
||
|
||
Properties:
|
||
- string PageKey (unique, e.g. "about", "contact") — کلید شناسایی
|
||
- string Title (max 200)
|
||
- string? MetaDescription (max 300) — SEO
|
||
- string? HeroTitle — عنوان Hero
|
||
- string? HeroSubtitle — زیرعنوان Hero
|
||
- string? HeroImagePath — تصویر Hero
|
||
- bool IsActive
|
||
|
||
Relations:
|
||
- ICollection<SitePageSection> Sections
|
||
```
|
||
|
||
#### `SitePageSection.cs` (بخشهای صفحه)
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Domain/Entities/Content/SitePageSection.cs
|
||
Base: BaseAuditableEntity
|
||
|
||
Properties:
|
||
- long SitePageId
|
||
- string SectionKey (e.g. "mission", "vision", "team-member-1")
|
||
- string Title (max 200)
|
||
- string? Subtitle (max 300)
|
||
- string? HtmlContent — محتوای HTML
|
||
- string? IconName — آیکون Material
|
||
- string? ImagePath
|
||
- string? ImageThumbnailPath
|
||
- int SortOrder
|
||
- bool IsActive
|
||
- string? ExtraData — JSON blob for flexible data
|
||
|
||
Relations:
|
||
- SitePage SitePage
|
||
```
|
||
|
||
#### Enum: `BlogPostStatus.cs`
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Domain/Enums/BlogPostStatus.cs
|
||
|
||
Values:
|
||
Draft = 0
|
||
Published = 1
|
||
Scheduled = 2
|
||
Archived = 3
|
||
```
|
||
|
||
### 5.2 Entity Configurations (Infrastructure)
|
||
|
||
```
|
||
فایلهای جدید:
|
||
CMS/src/CMSMicroservice.Infrastructure/Configurations/Blog/
|
||
├── BlogPostConfiguration.cs
|
||
├── BlogCategoryConfiguration.cs
|
||
├── BlogPostCategoryConfiguration.cs
|
||
├── BlogPostTagConfiguration.cs
|
||
└── BlogPostImageConfiguration.cs
|
||
CMS/src/CMSMicroservice.Infrastructure/Configurations/Content/
|
||
├── SitePageConfiguration.cs
|
||
└── SitePageSectionConfiguration.cs
|
||
|
||
فایلهای ویرایشی:
|
||
CMS/src/CMSMicroservice.Infrastructure/ApplicationDbContext.cs
|
||
+ DbSet<BlogPost> BlogPosts
|
||
+ DbSet<BlogCategory> BlogCategories
|
||
+ DbSet<BlogPostCategory> BlogPostCategories
|
||
+ DbSet<BlogPostTag> BlogPostTags
|
||
+ DbSet<BlogPostImage> BlogPostImages
|
||
+ DbSet<SitePage> SitePages
|
||
+ DbSet<SitePageSection> SitePageSections
|
||
```
|
||
|
||
### 5.3 Proto Definitions
|
||
|
||
#### `blogpost.proto`
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Protobuf/Protos/blogpost.proto
|
||
|
||
service BlogPostContract {
|
||
rpc CreateBlogPost (CreateBlogPostRequest) returns (BlogPostResponse);
|
||
rpc UpdateBlogPost (UpdateBlogPostRequest) returns (BlogPostResponse);
|
||
rpc DeleteBlogPost (DeleteBlogPostRequest) returns (BoolResponse);
|
||
rpc GetBlogPost (GetBlogPostRequest) returns (BlogPostResponse);
|
||
rpc GetBlogPostBySlug (GetBlogPostBySlugRequest) returns (BlogPostResponse);
|
||
rpc GetAllBlogPosts (GetAllBlogPostsRequest) returns (BlogPostListResponse);
|
||
rpc GetPublishedBlogPosts (GetPublishedBlogPostsRequest) returns (BlogPostListResponse);
|
||
rpc GetFeaturedBlogPosts (GetFeaturedBlogPostsRequest) returns (BlogPostListResponse);
|
||
rpc PublishBlogPost (PublishBlogPostRequest) returns (BoolResponse);
|
||
rpc ArchiveBlogPost (ArchiveBlogPostRequest) returns (BoolResponse);
|
||
rpc IncrementViewCount (IncrementViewCountRequest) returns (BoolResponse);
|
||
}
|
||
```
|
||
|
||
#### `blogcategory.proto`
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Protobuf/Protos/blogcategory.proto
|
||
|
||
service BlogCategoryContract {
|
||
rpc CreateBlogCategory (CreateBlogCategoryRequest) returns (BlogCategoryResponse);
|
||
rpc UpdateBlogCategory (UpdateBlogCategoryRequest) returns (BlogCategoryResponse);
|
||
rpc DeleteBlogCategory (DeleteBlogCategoryRequest) returns (BoolResponse);
|
||
rpc GetBlogCategory (GetBlogCategoryRequest) returns (BlogCategoryResponse);
|
||
rpc GetAllBlogCategories (GetAllBlogCategoriesRequest) returns (BlogCategoryListResponse);
|
||
}
|
||
```
|
||
|
||
#### `blogpostimage.proto`
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Protobuf/Protos/blogpostimage.proto
|
||
|
||
service BlogPostImageContract {
|
||
rpc AddBlogPostImage (AddBlogPostImageRequest) returns (BlogPostImageResponse);
|
||
rpc DeleteBlogPostImage (DeleteBlogPostImageRequest) returns (BoolResponse);
|
||
rpc GetBlogPostImages (GetBlogPostImagesRequest) returns (BlogPostImageListResponse);
|
||
rpc ReorderBlogPostImages (ReorderBlogPostImagesRequest) returns (BoolResponse);
|
||
}
|
||
```
|
||
|
||
#### `sitepage.proto`
|
||
```
|
||
Location: CMS/src/CMSMicroservice.Protobuf/Protos/sitepage.proto
|
||
|
||
service SitePageContract {
|
||
rpc GetSitePage (GetSitePageRequest) returns (SitePageResponse);
|
||
rpc GetSitePageByKey (GetSitePageByKeyRequest) returns (SitePageResponse);
|
||
rpc UpdateSitePage (UpdateSitePageRequest) returns (SitePageResponse);
|
||
rpc GetAllSitePages (GetAllSitePagesRequest) returns (SitePageListResponse);
|
||
rpc CreateSitePageSection (CreateSitePageSectionRequest) returns (SitePageSectionResponse);
|
||
rpc UpdateSitePageSection (UpdateSitePageSectionRequest) returns (SitePageSectionResponse);
|
||
rpc DeleteSitePageSection (DeleteSitePageSectionRequest) returns (BoolResponse);
|
||
rpc ReorderSitePageSections (ReorderSitePageSectionsRequest) returns (BoolResponse);
|
||
}
|
||
```
|
||
|
||
### 5.4 Application Layer (CQRS)
|
||
|
||
```
|
||
CMS/src/CMSMicroservice.Application/
|
||
BlogPostCQ/
|
||
Commands/
|
||
CreateBlogPost/
|
||
CreateBlogPostCommand.cs
|
||
CreateBlogPostCommandHandler.cs
|
||
CreateBlogPostCommandValidator.cs
|
||
UpdateBlogPost/
|
||
UpdateBlogPostCommand.cs
|
||
UpdateBlogPostCommandHandler.cs
|
||
UpdateBlogPostCommandValidator.cs
|
||
DeleteBlogPost/
|
||
DeleteBlogPostCommand.cs
|
||
DeleteBlogPostCommandHandler.cs
|
||
PublishBlogPost/
|
||
PublishBlogPostCommand.cs
|
||
PublishBlogPostCommandHandler.cs
|
||
ArchiveBlogPost/
|
||
ArchiveBlogPostCommand.cs
|
||
ArchiveBlogPostCommandHandler.cs
|
||
Queries/
|
||
GetBlogPost/
|
||
GetBlogPostQuery.cs
|
||
GetBlogPostQueryHandler.cs
|
||
GetBlogPostBySlug/
|
||
GetBlogPostBySlugQuery.cs
|
||
GetBlogPostBySlugQueryHandler.cs
|
||
GetAllBlogPosts/
|
||
GetAllBlogPostsQuery.cs
|
||
GetAllBlogPostsQueryHandler.cs
|
||
GetPublishedBlogPosts/
|
||
GetPublishedBlogPostsQuery.cs
|
||
GetPublishedBlogPostsQueryHandler.cs
|
||
GetFeaturedBlogPosts/
|
||
GetFeaturedBlogPostsQuery.cs
|
||
GetFeaturedBlogPostsQueryHandler.cs
|
||
|
||
BlogCategoryCQ/
|
||
Commands/
|
||
CreateBlogCategory/ (3 files)
|
||
UpdateBlogCategory/ (3 files)
|
||
DeleteBlogCategory/ (2 files)
|
||
Queries/
|
||
GetBlogCategory/ (2 files)
|
||
GetAllBlogCategories/ (2 files)
|
||
|
||
BlogPostImageCQ/
|
||
Commands/
|
||
AddBlogPostImage/ (3 files)
|
||
DeleteBlogPostImage/ (2 files)
|
||
ReorderBlogPostImages/ (2 files)
|
||
Queries/
|
||
GetBlogPostImages/ (2 files)
|
||
|
||
SitePageCQ/
|
||
Commands/
|
||
UpdateSitePage/ (3 files)
|
||
CreateSitePageSection/ (3 files)
|
||
UpdateSitePageSection/ (3 files)
|
||
DeleteSitePageSection/ (2 files)
|
||
ReorderSitePageSections/ (2 files)
|
||
Queries/
|
||
GetSitePage/ (2 files)
|
||
GetSitePageByKey/ (2 files)
|
||
GetAllSitePages/ (2 files)
|
||
```
|
||
|
||
### 5.5 WebApi Services & Mappings
|
||
|
||
```
|
||
CMS/src/CMSMicroservice.WebApi/
|
||
Services/
|
||
BlogPostService.cs — extends BlogPostContractBase
|
||
BlogCategoryService.cs — extends BlogCategoryContractBase
|
||
BlogPostImageService.cs — extends BlogPostImageContractBase
|
||
SitePageService.cs — extends SitePageContractBase
|
||
Common/Mappings/
|
||
BlogPostProfile.cs
|
||
BlogCategoryProfile.cs
|
||
BlogPostImageProfile.cs
|
||
SitePageProfile.cs
|
||
```
|
||
|
||
### 5.6 Migration
|
||
|
||
```bash
|
||
# ساخت Migration جدید
|
||
cd CMS/src/CMSMicroservice.Infrastructure
|
||
dotnet ef migrations add AddBlogAndSitePages \
|
||
--startup-project ../CMSMicroservice.WebApi \
|
||
--context ApplicationDbContext
|
||
```
|
||
|
||
---
|
||
|
||
## 6. فاز ۲ — پنل مدیریت بلاگ (BackOffice) ✅ تکمیل شد
|
||
|
||
> **تخمین:** ۲-۳ روز | **اولویت:** بالا | **پیشنیاز:** فاز ۱ | **وضعیت: تکمیل ✅**
|
||
|
||
### 6.1 صفحات ایجاد شده BackOffice
|
||
|
||
```
|
||
BackOffice/src/BackOffice/Pages/
|
||
Blog/
|
||
BlogPostManagementPage.razor — لیست پستها + فیلتر وضعیت/دستهبندی + جستجو
|
||
BlogPostManagementPage.razor.cs — کدبیهایند: LoadServerData، CRUD، Publish، Archive
|
||
BlogCategoryManagementPage.razor — مدیریت دستهبندیها (MudDataGrid)
|
||
BlogCategoryManagementPage.razor.cs
|
||
Components/
|
||
BlogPostEditDialog.razor — ایجاد/ویرایش پست (عنوان، اسلاگ، خلاصه، HTML، دستهبندیها)
|
||
BlogPostEditDialog.razor.cs — Multi-select دستهبندی + ویژه/ترتیب
|
||
BlogCategoryEditDialog.razor — ایجاد/ویرایش دستهبندی
|
||
BlogCategoryEditDialog.razor.cs
|
||
|
||
Content/
|
||
SitePageManagementPage.razor — لیست صفحات سایت
|
||
SitePageManagementPage.razor.cs — ویرایش صفحه + مدیریت بخشها
|
||
Components/
|
||
SitePageEditDialog.razor — ویرایش اطلاعات صفحه (Hero, Meta, وضعیت)
|
||
SitePageEditDialog.razor.cs
|
||
SitePageSectionsDialog.razor — مدیریت بخشهای صفحه (CRUD + لیست)
|
||
SitePageSectionsDialog.razor.cs
|
||
SitePageSectionEditDialog.razor — ایجاد/ویرایش بخش (کلید، عنوان، HTML، تصاویر)
|
||
SitePageSectionEditDialog.razor.cs
|
||
```
|
||
|
||
### 6.2 ناوبری BackOffice (NavMenu) ✅
|
||
|
||
بخش «مدیریت محتوا» به `NavMenu.razor` اضافه شد (قبل از بخش سیستم):
|
||
|
||
```razor
|
||
<MudText Class="nav-menu__title" Typo="Typo.subtitle2">مدیریت محتوا</MudText>
|
||
|
||
<AuthorizeView Roles="Administrator">
|
||
<Authorized>
|
||
@if (CanManageBlog)
|
||
{
|
||
<MudNavGroup Title="بلاگ" Icon="@Icons.Material.Filled.Article" Expanded="false">
|
||
<MudNavLink Href="/blog/posts" Icon="@Icons.Material.Filled.PostAdd">پستها</MudNavLink>
|
||
<MudNavLink Href="/blog/categories" Icon="@Icons.Material.Filled.Category">دستهبندیها</MudNavLink>
|
||
</MudNavGroup>
|
||
}
|
||
|
||
@if (CanManageSitePages)
|
||
{
|
||
<MudNavLink Href="/content/pages" Icon="@Icons.Material.Filled.WebAsset">صفحات سایت</MudNavLink>
|
||
}
|
||
</Authorized>
|
||
</AuthorizeView>
|
||
```
|
||
|
||
مجوزهای جدید: `blog.manage`، `sitepages.manage`
|
||
|
||
### 6.3 سرویسهای BackOffice ✅
|
||
|
||
```
|
||
BackOffice/src/BackOffice/Services/
|
||
Blog/
|
||
IBlogCategoryService.cs — اینترفیس + DTOs (Filter, ListResult, Item, Edit)
|
||
BlogCategoryService.cs — پیادهسازی با gRPC client (GetAll, GetActive, GetById, Create, Update, Delete)
|
||
IBlogPostService.cs — اینترفیس + DTOs (Filter, ListResult, ListItem, Details, Edit, PublishResult, ArchiveResult)
|
||
BlogPostService.cs — پیادهسازی (CRUD + Publish + Archive)
|
||
IBlogPostImageService.cs — اینترفیس + DTOs (ImageItem, ImageAdd, ImageSort)
|
||
BlogPostImageService.cs — پیادهسازی (GetByPostId, Add, Delete, Reorder)
|
||
Content/
|
||
ISitePageService.cs — اینترفیس + DTOs (Summary, Details, SectionItem, Edit, SectionEdit, SectionSort)
|
||
SitePageService.cs — پیادهسازی (GetAll, GetById, Update, CRUD Section, Reorder)
|
||
```
|
||
|
||
ثبت DI در `ConfigureService.cs`:
|
||
- ۴ gRPC Client: `BlogPostContractClient`، `BlogCategoryContractClient`، `BlogPostImageContractClient`، `SitePageContractClient`
|
||
- ۴ Application Service: `IBlogPostService`، `IBlogCategoryService`، `IBlogPostImageService`، `ISitePageService`
|
||
|
||
### 6.4 ویرایشگر محتوا
|
||
|
||
از پکیج موجود `Tizzani.MudBlazor.HtmlEditor` (قبلاً نصب شده) استفاده میشود:
|
||
|
||
```razor
|
||
<!-- در CreateBlogPostDialog -->
|
||
<MudHtmlEditor @bind-Value="_post.HtmlContent"
|
||
Placeholder="محتوای مقاله را بنویسید..."
|
||
MinHeight="400px" />
|
||
```
|
||
|
||
### 6.5 آپلود تصویر شاخص
|
||
|
||
از سرویس موجود `IFileManagerService` استفاده:
|
||
|
||
```csharp
|
||
// الگوی موجود از ProductService
|
||
var imagePath = await _fileManagerService.UploadFileAsync(imageBytes, fileName);
|
||
```
|
||
|
||
### 6.6 مشخصات صفحه مدیریت مقالات
|
||
|
||
| ویژگی | شرح |
|
||
|-------|------|
|
||
| جدول مقالات | MudDataGrid با ستونهای: عنوان، دستهبندی، وضعیت، تاریخ انتشار، بازدید |
|
||
| فیلتر | وضعیت (همه/پیشنویس/منتشر/آرشیو) + جستجوی عنوان |
|
||
| عملیات | ایجاد، ویرایش، حذف، انتشار، آرشیو، پیشنمایش |
|
||
| تصویر شاخص | Drag & Drop آپلود + پیشنمایش |
|
||
| دستهبندی | Multi-select از دستهبندیهای موجود |
|
||
| تگ | Multi-select با autocomplete از تگهای موجود |
|
||
| ویرایشگر | WYSIWYG HTML editor (MudBlazor.HtmlEditor) |
|
||
|
||
---
|
||
|
||
## 7. فاز ۳ — صفحات محتوای دینامیک (درباره ما / تماس با ما) ✅ تکمیل شد
|
||
|
||
> **وضعیت:** ✅ تکمیل | **اولویت:** متوسط | **پیشنیاز:** فاز ۱
|
||
|
||
### 7.1 تغییرات About.razor
|
||
|
||
**قبل (فعلی):** محتوا هاردکد — عنوانها، تصاویر، متون همه در Razor مستقیم نوشته شده
|
||
|
||
**بعد (جدید):**
|
||
```
|
||
OnInitializedAsync:
|
||
1. صدا زدن SitePageContract.GetSitePageByKey("about")
|
||
2. دریافت SitePage + Sections
|
||
3. نمایش دینامیک با loop روی Sections
|
||
|
||
Fallback:
|
||
- اگر سرویس خطا داد → نمایش محتوای پیشفرض (هاردکد فعلی به عنوان fallback)
|
||
```
|
||
|
||
ساختار بخشهای صفحه درباره ما:
|
||
|
||
| SectionKey | نوع | داده |
|
||
|-----------|-----|------|
|
||
| `hero` | Hero | HeroTitle, HeroSubtitle, HeroImagePath |
|
||
| `mission` | Card | Title, HtmlContent, IconName |
|
||
| `vision` | Card | Title, HtmlContent, IconName |
|
||
| `value-1` ... `value-N` | Cards Grid | Title, Subtitle, IconName, HtmlContent |
|
||
| `team-1` ... `team-N` | Team Cards | Title (نام), Subtitle (سمت), HtmlContent (توضیح), ImagePath |
|
||
|
||
### 7.2 تغییرات Contact.razor
|
||
|
||
| SectionKey | نوع | داده |
|
||
|-----------|-----|------|
|
||
| `hero` | Hero | HeroTitle, HeroSubtitle |
|
||
| `contact-info` | Info | ExtraData (JSON: address, phone, email, hours) |
|
||
| `social-media` | Links | ExtraData (JSON: telegram, instagram, linkedin, whatsapp urls) |
|
||
| `map` | Map | ExtraData (JSON: lat, lng, address text) |
|
||
|
||
### 7.3 Seed Data
|
||
|
||
اجرای اولیه Migration با داده پیشفرض (محتوای هاردکد فعلی):
|
||
|
||
```csharp
|
||
// در SitePageConfiguration یا Seed Migration
|
||
var aboutPage = new SitePage {
|
||
PageKey = "about",
|
||
Title = "درباره ما",
|
||
HeroTitle = "پلتفرم هوشمند تیمسازی و مدیریت فروش",
|
||
HeroSubtitle = "ما با ارائه ابزارهای نوآورانه...",
|
||
IsActive = true
|
||
};
|
||
// + Sections با محتوای فعلی
|
||
```
|
||
|
||
---
|
||
|
||
## 8. فاز ۴ — نوسازی لندینگ پیج ✅ تکمیل شد
|
||
|
||
> **وضعیت:** ✅ تکمیل | **اولویت:** بالا | **پیشنیاز:** فاز ۱ (بخش بلاگ برای "آخرین مقالات")
|
||
|
||
### 8.1 ساختار جدید لندینگ پیج
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ 🔝 HERO SECTION │
|
||
│ • Gradient animated background │
|
||
│ • عنوان بزرگ + زیرعنوان │
|
||
│ • Badgeهای انیمیشنی │
|
||
│ • CTA buttons با hover effects │
|
||
│ • تصویر/ایلاستریشن سمت چپ │
|
||
│ • اعداد زنده (تعداد کاربران، ...) │
|
||
└─────────────────────────────────────────┘
|
||
↓ Scroll indicator ↓
|
||
┌─────────────────────────────────────────┐
|
||
│ ✨ FEATURES (چرا ما؟) │
|
||
│ • 6 کارت با آیکون + عنوان + توضیح │
|
||
│ • Hover effect (scale + shadow) │
|
||
│ • انیمیشن fade-in on scroll │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ 📊 LIVE STATS (آمار زنده) │
|
||
│ • Counter animation (شمارش تعداد) │
|
||
│ • تعداد کاربران فعال │
|
||
│ • تعداد محصولات │
|
||
│ • حجم معاملات │
|
||
│ • Intersection Observer trigger │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ 🔄 HOW IT WORKS (چطور کار میکند) │
|
||
│ • Timeline بهبودیافته │
|
||
│ • آیکونهای مرحلهای بزرگتر │
|
||
│ • انیمیشن step-by-step │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ 📝 LATEST BLOG (آخرین مقالات) │ ← جدید!
|
||
│ • ۳ کارت آخرین مقالات منتشرشده │
|
||
│ • تصویر شاخص + عنوان + خلاصه │
|
||
│ • دکمه "مشاهده همه مقالات" │
|
||
│ • Loading skeleton │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ 🏆 TESTIMONIALS (اعتماد مشتریان) │
|
||
│ • Carousel/Swiper اسلایدر │
|
||
│ • Avatar + نام + سمت + نقل قول │
|
||
│ • Auto-play + دکمههای ناوبری │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ ❓ FAQ (سوالات متداول) │
|
||
│ • بدون تغییر اساسی (فقط پولیش) │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ 📞 CTA BANNER (دعوت نهایی) │ ← جدید!
|
||
│ • Gradient background │
|
||
│ • عنوان جذاب + دکمه ثبتنام │
|
||
│ • طرح ساده و تأثیرگذار │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ 🦶 FOOTER │
|
||
│ • بدون تغییر اساسی + لینک بلاگ │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
### 8.2 تغییرات فنی لندینگ
|
||
|
||
#### الف) Hero Section بهبودیافته
|
||
|
||
```
|
||
تغییرات:
|
||
1. Animated gradient background (CSS keyframes)
|
||
2. اعداد واقعی از API (تعداد کاربران، محصولات، ...)
|
||
3. Badgeها با انیمیشن pulse
|
||
4. Scroll-down indicator (chevron انیمیشنی)
|
||
5. تصویر/SVG illustration سمت چپ
|
||
```
|
||
|
||
#### ب) آمار زنده (Live Stats)
|
||
|
||
```
|
||
سرویس جدید — یک Query ساده در CMS:
|
||
GetPlatformStats → { UserCount, ProductCount, OrderCount, ... }
|
||
|
||
فرانت:
|
||
- Counter animation (JS interop یا CSS counter)
|
||
- Intersection Observer: فقط وقتی اسکرول به این بخش رسید شمارش شروع شود
|
||
```
|
||
|
||
#### ج) بخش آخرین مقالات (جدید)
|
||
|
||
```razor
|
||
<!-- Latest Blog Posts Section -->
|
||
<section class="py-16">
|
||
<MudContainer MaxWidth="MaxWidth.Large">
|
||
<MudText Typo="Typo.h4" Align="Align.Center">آخرین مقالات</MudText>
|
||
<MudGrid Spacing="4">
|
||
@foreach (var post in _latestPosts.Take(3))
|
||
{
|
||
<MudItem xs="12" md="4">
|
||
<BlogPostCard Post="post" />
|
||
</MudItem>
|
||
}
|
||
</MudGrid>
|
||
<MudButton Href="/blog" Variant="Variant.Outlined">مشاهده همه</MudButton>
|
||
</MudContainer>
|
||
</section>
|
||
```
|
||
|
||
#### د) CTA Banner نهایی (جدید)
|
||
|
||
```razor
|
||
<!-- Final CTA -->
|
||
<section class="cta-banner py-16">
|
||
<MudContainer MaxWidth="MaxWidth.Medium">
|
||
<MudStack AlignItems="AlignItems.Center" Spacing="4">
|
||
<MudText Typo="Typo.h3">آماده شروع هستید؟</MudText>
|
||
<MudText>همین الان ثبتنام کنید و از مزایای کارا بازار سلامت بهرهمند شوید.</MudText>
|
||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Size="Size.Large">
|
||
شروع رایگان
|
||
</MudButton>
|
||
</MudStack>
|
||
</MudContainer>
|
||
</section>
|
||
```
|
||
|
||
### 8.3 CSS جدید / بهبودیافته
|
||
|
||
```css
|
||
/* فایل: wwwroot/css/app.css — بخشهای جدید */
|
||
|
||
/* Animated gradient hero */
|
||
.hero-section {
|
||
background: linear-gradient(-45deg, #6366f1, #8b5cf6, #a855f7, #6366f1);
|
||
background-size: 400% 400%;
|
||
animation: gradientShift 8s ease infinite;
|
||
}
|
||
|
||
@keyframes gradientShift {
|
||
0% { background-position: 0% 50%; }
|
||
50% { background-position: 100% 50%; }
|
||
100% { background-position: 0% 50%; }
|
||
}
|
||
|
||
/* Scroll-triggered fade-in */
|
||
.fade-in-up {
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
transition: opacity 0.6s ease, transform 0.6s ease;
|
||
}
|
||
.fade-in-up.visible {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
/* Counter animation */
|
||
@property --num {
|
||
syntax: '<integer>';
|
||
initial-value: 0;
|
||
inherits: false;
|
||
}
|
||
.counter {
|
||
animation: counter 2s ease-out forwards;
|
||
counter-reset: num var(--num);
|
||
}
|
||
|
||
/* Pulse badge */
|
||
.pulse-badge {
|
||
animation: pulse 2s infinite;
|
||
}
|
||
@keyframes pulse {
|
||
0% { box-shadow: 0 0 0 0 rgba(99,102,241,0.4); }
|
||
70% { box-shadow: 0 0 0 10px rgba(99,102,241,0); }
|
||
100% { box-shadow: 0 0 0 0 rgba(99,102,241,0); }
|
||
}
|
||
|
||
/* CTA Banner */
|
||
.cta-banner {
|
||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||
color: white;
|
||
border-radius: 24px;
|
||
margin: 2rem;
|
||
}
|
||
|
||
/* Card hover effects */
|
||
.feature-card-v2 {
|
||
transition: transform 0.3s, box-shadow 0.3s;
|
||
}
|
||
.feature-card-v2:hover {
|
||
transform: translateY(-8px);
|
||
box-shadow: 0 12px 40px rgba(0,0,0,0.12);
|
||
}
|
||
|
||
/* Blog post card */
|
||
.blog-card {
|
||
transition: transform 0.3s ease;
|
||
overflow: hidden;
|
||
}
|
||
.blog-card:hover {
|
||
transform: translateY(-4px);
|
||
}
|
||
.blog-card .blog-card-image {
|
||
transition: transform 0.5s ease;
|
||
}
|
||
.blog-card:hover .blog-card-image {
|
||
transform: scale(1.05);
|
||
}
|
||
```
|
||
|
||
### 8.4 JS Interop (حداقل)
|
||
|
||
```javascript
|
||
// فایل: wwwroot/js/landing.js
|
||
|
||
// Intersection Observer for fade-in animations
|
||
window.initScrollAnimations = () => {
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add('visible');
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
}, { threshold: 0.1 });
|
||
|
||
document.querySelectorAll('.fade-in-up').forEach(el => observer.observe(el));
|
||
};
|
||
|
||
// Counter animation
|
||
window.animateCounter = (elementId, target, duration) => {
|
||
const el = document.getElementById(elementId);
|
||
if (!el) return;
|
||
let start = 0;
|
||
const step = target / (duration / 16);
|
||
const timer = setInterval(() => {
|
||
start += step;
|
||
if (start >= target) { start = target; clearInterval(timer); }
|
||
el.textContent = Math.floor(start).toLocaleString('fa-IR');
|
||
}, 16);
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 9. فاز ۵ — صفحات بلاگ فرانتآفیس ✅ تکمیل شد
|
||
|
||
> **وضعیت:** ✅ تکمیل | **اولویت:** بالا | **پیشنیاز:** فاز ۱
|
||
|
||
### 9.1 صفحات ایجاد شده (واقعی)
|
||
|
||
```
|
||
FrontOffice/src/FrontOffice.Main/Pages/Blog/
|
||
Index.razor — لیست مقالات با جستجو + فیلتر دستهبندی + صفحهبندی
|
||
Index.razor.cs — LoadPostsAsync، OnCategoryChanged، OnPageChanged، NavigateToPost
|
||
Post.razor — صفحه جزئیات مقاله (تصویر شاخص، breadcrumb، HTML، تگها)
|
||
Post.razor.cs — OnParametersSetAsync (لود slug)، IncrementViewCount خودکار
|
||
```
|
||
|
||
### 9.2 سرویسهای جدید فرانتآفیس
|
||
|
||
```
|
||
FrontOffice/src/FrontOffice.Main/Utilities/
|
||
BlogPostService.cs — (قبلاً فاز ۴ ایجاد شد) GetFeatured, GetPublished, GetBySlug, IncrementView
|
||
BlogCategoryService.cs — GetActiveCategoriesAsync (فیلتر سایدبار بلاگ)
|
||
```
|
||
|
||
**DTOs تعریف شده در `BlogPostService.cs`:**
|
||
- `BlogPostCardDto` — Id, Title, Slug, Summary, ThumbnailUrl, PublishedAt, ViewCount, IsFeatured, Categories
|
||
- `BlogPostDetailDto` — همه فیلدهای کارت + HtmlContent, FeaturedImagePath, Tags
|
||
- `BlogPostListResult` — Posts, TotalCount, TotalPages, CurrentPage
|
||
- `BlogCategoryInfo` — Id, Title, Slug
|
||
- `BlogTagInfo` — Id, Title, Name
|
||
|
||
**DTO تعریف شده در `BlogCategoryService.cs`:**
|
||
- `BlogCategoryDto` — Id, Title, Slug, Description, IconName, PostCount
|
||
|
||
### 9.3 مسیرها (Routes) — پیادهسازی شده
|
||
|
||
```csharp
|
||
// RouteConstants.cs
|
||
public static class Blog
|
||
{
|
||
public const string Index = "/blog";
|
||
public const string Post = "/blog/"; // usage: /blog/{slug}
|
||
}
|
||
```
|
||
|
||
### 9.4 طراحی صفحه لیست بلاگ — پیادهسازی شده
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ Blog Hero (گرادیان بنفش) │
|
||
│ "بلاگ" │
|
||
│ "آخرین مطالب، آموزشها و اخبار" │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ [جستجو...] [دستهبندی ▼] [جستجو 🔍] │ ← فیلتر + MudSelect
|
||
└─────────────────────────────────────────┘
|
||
┌────────┐ ┌────────┐ ┌────────┐
|
||
│ Card 1 │ │ Card 2 │ │ Card 3 │ ← blog-card-v2
|
||
│ thumb │ │ thumb │ │ thumb │ ← تصویر شاخص
|
||
│ cat │ │ cat │ │ cat │ ← chip دستهبندی
|
||
│ title │ │ title │ │ title │ ← ۲ خط clamp
|
||
│ desc │ │ desc │ │ desc │ ← ۲ خط clamp
|
||
│ 📅 👁 │ │ 📅 👁 │ │ 📅 👁 │ ← تاریخ + بازدید
|
||
└────────┘ └────────┘ └────────┘
|
||
← ← ← [1] [2] [3] → → → ← MudPagination
|
||
```
|
||
|
||
### 9.5 طراحی صفحه مقاله — پیادهسازی شده
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ [تصویر شاخص full-width] + overlay │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ خانه > بلاگ > عنوان مقاله │ ← Breadcrumb
|
||
│ │
|
||
│ [دستهبندی ۱] [دستهبندی ۲] │ ← Chips (کلیک → فیلتر)
|
||
│ │
|
||
│ عنوان مقاله (H4, bold) │
|
||
│ خلاصه مقاله (subtitle) │
|
||
│ │
|
||
│ 📅 تاریخ 👁 ۱,۲۳۴ بازدید │
|
||
│ ───────────────────────── │
|
||
│ │
|
||
│ [محتوای HTML مقاله] │ ← blog-content (styled)
|
||
│ ... │
|
||
│ │
|
||
│ ───────────────────────── │
|
||
│ 🏷️ [تگ ۱] [تگ ۲] [تگ ۳] │ ← Outlined chips
|
||
│ │
|
||
│ [← بازگشت به بلاگ] │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
### 9.6 CSS اضافه شده (بخش Blog Pages در site.css)
|
||
|
||
```css
|
||
/* Blog Pages region — اضافه شده به site.css */
|
||
.blog-hero — گرادیان بنفش (مشابه CTA)
|
||
.blog-title-clamp — -webkit-line-clamp: 2
|
||
.blog-summary-clamp — -webkit-line-clamp: 2
|
||
.cursor-pointer — cursor: pointer
|
||
.blog-detail-hero — تصویر شاخص 340px + overlay
|
||
.blog-content — تایپوگرافی مقاله (h1-h3, p, img, blockquote, code, pre, ul/ol, a)
|
||
/* + mobile overrides + dark mode */
|
||
```
|
||
|
||
### 9.7 ناوبری — لینک بلاگ اضافه شد ✅
|
||
|
||
```
|
||
تغییرات واقعی:
|
||
- MainLayout.razor → لینک "بلاگ" در navbar دسکتاپ (بین FAQ و Contact)
|
||
- MainLayout.razor → لینک "بلاگ" در Mobile Drawer
|
||
- Index.razor (لندینگ) → کارتهای بلاگ کلیکپذیر شدند (onclick)
|
||
- Index.razor (لندینگ) → دکمه "مشاهده همه مقالات" فعال شد (uncomment)
|
||
```
|
||
|
||
### 9.8 ثبت DI (ConfigureServices.cs)
|
||
|
||
```csharp
|
||
// اضافه شده:
|
||
using CMSMicroservice.Protobuf.Protos.BlogCategory;
|
||
|
||
// Common services:
|
||
services.AddScoped<BlogCategoryService>();
|
||
|
||
// gRPC clients:
|
||
services.AddScoped(CreateAuthenticatedClient<BlogCategoryContract.BlogCategoryContractClient>);
|
||
```
|
||
|
||
---
|
||
|
||
## 10. فاز ۶ — بهبود داشبورد کاربر ✅ تکمیل شد
|
||
|
||
> **وضعیت:** ✅ تکمیل | **اولویت:** متوسط | **پیشنیاز:** ندارد
|
||
|
||
### 10.1 بازطراحی کامل Profile/Index.razor
|
||
|
||
صفحه `/profile` به طور کامل با طراحی مدرن بازنویسی شد:
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ ████ GRADIENT HEADER CARD ████ │ ← .dash-header-card
|
||
│ 🧑 آواتار شیشهای | نام + موبایل │ ← .dash-avatar
|
||
│ عضو از ... | [💎 سامانه دایا] │ ← دکمه glass-morphism
|
||
└─────────────────────────────────────────┘
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ 💳 اعتباری│ │ 🏷 تخفیف │ │ 👥 تیمی │ ← .dash-wallet-card
|
||
│ ۲.۵M تومان│ │ ۵۰۰K │ │ ۸۵۰K │ (hover lift)
|
||
└──────────┘ └──────────┘ └──────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ 📎 کد دعوت شما | [اشتراکگذاری] │ ← فعال اگر پکیج + باشگاه
|
||
│ یا │
|
||
│ 🔒 لینک دعوت فعال نشده (dashed border)│ ← حالت قفل
|
||
│ [شروع فرآیند تامین اعتبار] │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ دسترسی سریع │ ← .dash-tile (10 تایل)
|
||
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
|
||
│ │شخصی│ │آدرس│ │شجره│ │تنظیم│ │ هر تایل:
|
||
│ └────┘ └────┘ └────┘ └────┘ │ - آواتار رنگی
|
||
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ - عنوان + زیرعنوان
|
||
│ │فروش│ │کیف │ │برداشت│ │باشگا│ │ - hover: lift + primary border
|
||
│ └────┘ └────┘ └────┘ └────┘ │
|
||
│ ┌────┐ ┌────┐ │
|
||
│ │آمار│ │پاداش│ │
|
||
│ └────┘ └────┘ │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
### 10.2 تغییرات فایلها
|
||
|
||
**`Profile/Index.razor`** — بازنویسی کامل:
|
||
- هدر گرادیانی (`dash-header-card`) با آواتار شیشهای
|
||
- ۳ کارت والت جداگانه (اعتباری/تخفیف/تیمی) با رنگهای مختلف
|
||
- بخش رفرال تمیزتر (فعال با TextField + دکمه | قفل با dashed border)
|
||
- ۱۰ تایل مدرن با `DashTile` record + آواتار رنگی
|
||
|
||
**`Profile/Index.razor.cs`** — اضافه شده:
|
||
```csharp
|
||
private readonly List<DashTile> _dashTiles = new()
|
||
{
|
||
new(RouteConstants.Profile.Personal, Icons.Material.Filled.Person, "اطلاعات شخصی", ...),
|
||
new(RouteConstants.Profile.Addresses, Icons.Material.Filled.LocationOn, "آدرسها", ...),
|
||
// ... 10 تایل با رنگبندی اختصاصی
|
||
};
|
||
private record DashTile(string Href, string Icon, string Title, string Subtitle, string AvatarStyle);
|
||
```
|
||
|
||
### 10.3 CSS اضافه شده (بخش Dashboard v2 در site.css)
|
||
|
||
```css
|
||
/* Dashboard v2 region */
|
||
.dash-header-card — گرادیان بنفش + دایره تزئینی
|
||
.dash-avatar — شیشهای (rgba + border سفید)
|
||
.dash-wallet-card — border + hover lift
|
||
.dash-tile — border + hover lift + primary border on hover
|
||
/* + dark mode + mobile overrides */
|
||
```
|
||
|
||
---
|
||
|
||
## 11. فاز ۷ — بهینهسازی موبایل ✅ تکمیل شد
|
||
|
||
> **وضعیت:** ✅ تکمیل | **اولویت:** بالا | **پیشنیاز:** فازهای ۴-۶
|
||
|
||
### 11.1 مشکلات شناسایی و رفع شده
|
||
|
||
| مشکل | وضعیت قبل | راهحل |
|
||
|------|----------|--------|
|
||
| **Dead Zone ناوبری (600-959px)** | همبرگر فقط زیر 600px، لینکها فقط بالای 960px | تغییر Breakpoint از `SmAndUp` به `MdAndUp` |
|
||
| **فوتر پنهان در موبایل** | `DeviceDetector.IsDesktop()` فوتر را حذف میکرد | حذف شرط — فوتر همیشه نمایش داده شود |
|
||
| **عدم وجود Bottom Nav** | فقط Drawer برای ناوبری موبایل | اضافه شدن bottom-nav ثابت (5 لینک) |
|
||
| **تایپوگرافی بزرگ** | h1=2rem, h2=1.875rem ثابت | responsive: h1→1.5rem, h2→1.35rem در موبایل |
|
||
| **Touch targets کوچک** | Dense AppBar، لینکهای Drawer بدون padding | min-height: 44px برای لینکها، 40px برای آیکونها |
|
||
| **نقشه تماس بلند** | 400px ثابت | 240px در موبایل با کلاس `contact-map-placeholder` |
|
||
| **دکمههای About تنگ** | Row بدون flex-wrap | اضافه flex-wrap + full-width در موبایل |
|
||
| **محتوای بلاگ overflow** | بدون guard | `overflow-x: auto` + table/iframe responsive |
|
||
| **هاور در تاچ** | جلوههای هاور در صفحه لمسی | `@media (hover: none)` — غیرفعال |
|
||
| **Safe area** | بدون padding | `env(safe-area-inset-*)` برای notched phones |
|
||
| **theme-color** | نداشت | `<meta name="theme-color" content="#7c4dff">` |
|
||
|
||
### 11.2 Bottom Navigation — پیادهسازی
|
||
|
||
```
|
||
┌──────────────────────────────────────┐
|
||
│ 🏠 🏬 📰 🛒 👤 │ ← .bottom-nav (fixed)
|
||
│ خانه فروش بلاگ سبد پروفایل │ backdrop-filter blur
|
||
└──────────────────────────────────────┘
|
||
```
|
||
|
||
- فقط در زیر `Breakpoint.MdAndUp` نمایش داده شود (`MudHidden`)
|
||
- اگر لاگین نباشد: دکمه "ورود" بجای "سبد خرید" و "پروفایل"
|
||
- `padding-bottom: 68px` روی `.main-content-wrapper` برای جلوگیری از overlap
|
||
- `env(safe-area-inset-bottom)` برای گوشیهای notched
|
||
|
||
### 11.3 CSS اضافه شده (بخش Phase 7 در site.css ~120 خط)
|
||
|
||
```
|
||
.bottom-nav — fixed, z-1100, rounded top, glass blur
|
||
.bottom-nav-item — flex column, min 48px touch, transition color
|
||
.main-content-wrapper — padding-bottom 68px (≤960px)
|
||
responsive typography — h1-h5 smaller ≤600px
|
||
touch targets — drawer links 44px, appbar icons 40px
|
||
footer mobile — compact padding, smaller text
|
||
landing mobile — smaller chips, timeline text
|
||
blog mobile — shorter thumbnails, scaled pagination, overflow guard
|
||
dashboard mobile — compact wallets + tiles
|
||
about mobile — hero button wrap, reduced padding
|
||
contact mobile — shorter map, full-width submit
|
||
global mobile — tighter container padding, safe-area
|
||
@media (hover: none) — disable hover transforms on touch
|
||
```
|
||
|
||
### 11.4 فایلهای تغییریافته
|
||
|
||
```
|
||
MainLayout.razor — Breakpoint fix + Bottom Nav + Footer gate removed
|
||
site.css — +120 خط CSS موبایل (Phase 7 region)
|
||
_Host.cshtml — +theme-color + apple-mobile-web-app meta tags
|
||
Contact.razor — +class contact-map-placeholder
|
||
About.razor — +flex-wrap روی دکمههای hero
|
||
```
|
||
|
||
---
|
||
|
||
## 12. فایلهای تغییریافته (نقشه فایلها)
|
||
|
||
### فایلهای جدید
|
||
|
||
| پروژه | مسیر | تعداد فایل |
|
||
|--------|------|-----------|
|
||
| **CMS Domain** | `Entities/Blog/` (BlogPost, BlogCategory, BlogTag, BlogPostCategory, BlogPostTag, BlogPostImage) + `Entities/Content/` (SitePage, SitePageAttachment) + `Enums/` (BlogPostStatus) | **9** |
|
||
| **CMS Infrastructure** | `Configurations/Blog/` (5) + `Configurations/Content/` (2) + EF Migration | **8** |
|
||
| **CMS Application** | `BlogPostCQ/` (~25) + `BlogCategoryCQ/` (~12) + `BlogPostImageCQ/` (~9) + `SitePageCQ/` (~18) | **~64** |
|
||
| **CMS Protobuf** | `public_messages.proto` + `blogpost.proto` + `blogcategory.proto` + `sitepage.proto` | **4** |
|
||
| **CMS WebApi** | `Services/` (BlogPostService, BlogCategoryService, SitePageService, BlogPostImageService) + `Mappings/` (4 profiles) | **8** |
|
||
| **BackOffice Pages** | `Pages/Blog/` (BlogPosts.razor, BlogCategories.razor + ۲ dialog) + `Pages/Content/` (SitePages.razor + ۳ dialog) | **10** |
|
||
| **BackOffice Services** | `Services/Blog/` (BlogPostService, BlogCategoryService, BlogTagService + ۳ model) + `Services/Content/` (SitePageService + model) | **8** |
|
||
| **FrontOffice Pages** | `Pages/Blog/Index.razor` + `Index.razor.cs` + `Post.razor` + `Post.razor.cs` | **4** |
|
||
| **FrontOffice Services** | `Utilities/BlogPostService.cs` + `Utilities/BlogCategoryService.cs` + `Utilities/SitePageService.cs` | **3** |
|
||
| **FrontOffice Assets** | `wwwroot/js/landing.js` | **1** |
|
||
| | | **~119 فایل جدید** |
|
||
|
||
### فایلهای ویرایشی
|
||
|
||
| پروژه | فایل | تغییر |
|
||
|--------|------|-------|
|
||
| CMS Infrastructure | `ApplicationDbContext.cs` | +7 DbSets (BlogPost, BlogCategory, BlogTag, BlogPostCategory, BlogPostTag, BlogPostImage, SitePage) |
|
||
| CMS WebApi | `Program.cs` | +4 gRPC service registrations |
|
||
| BackOffice | `Shared/NavMenu.razor` | +بخش مدیریت محتوا (بلاگ + صفحات) + ۲ مجوز جدید |
|
||
| BackOffice | `Common/Configure/ConfigureService.cs` | +4 gRPC clients + 4 services + using statements |
|
||
| FrontOffice | `Pages/Index.razor` | بازنویسی کامل (Hero + Features + Timeline + Stats + Blog + Testimonials + FAQ + CTA) |
|
||
| FrontOffice | `Pages/Index.razor.cs` | +LoadFeaturedPosts + NavigateToPost |
|
||
| FrontOffice | `Pages/About.razor` | تبدیل به دینامیک (CMS + fallback) |
|
||
| FrontOffice | `Pages/Contact.razor` | تبدیل به دینامیک (CMS + fallback) |
|
||
| FrontOffice | `Pages/Profile/Index.razor` | بازنویسی کامل (header card + wallets + tiles) |
|
||
| FrontOffice | `Pages/Profile/Index.razor.cs` | +DashTile record + _dashTiles لیست |
|
||
| FrontOffice | `Shared/MainLayout.razor` | +لینک بلاگ (desktop + mobile drawer) |
|
||
| FrontOffice | `Common/Configure/ConfigureServices.cs` | +BlogCategoryService + BlogCategoryContract gRPC |
|
||
| FrontOffice | `Utilities/RouteConstants.cs` | +Blog.Index + Blog.Post routes |
|
||
| FrontOffice | `wwwroot/css/site.css` | +Landing v2 + Blog Pages + Dashboard v2 (~300 خط CSS) |
|
||
| FrontOffice | `Pages/_Host.cshtml` | +landing.js script reference |
|
||
| | | **~15 فایل ویرایشی** |
|
||
|
||
---
|
||
|
||
## 13. جدول زمانی
|
||
|
||
```
|
||
هفته ۱:
|
||
├─ فاز ۱: موجودیتهای بکند (Entities + Protos + CQRS) [۲-۳ روز] ✅ تکمیل
|
||
└─ فاز ۲: پنل مدیریت بلاگ BackOffice [۲-۳ روز] ✅ تکمیل
|
||
|
||
هفته ۲:
|
||
├─ فاز ۳: صفحات دینامیک (About + Contact) [۱-۲ روز] ✅ تکمیل
|
||
├─ فاز ۴: نوسازی لندینگ پیج [۲-۳ روز] ✅ تکمیل
|
||
└─ فاز ۵: صفحات بلاگ FrontOffice [۲-۳ روز] ✅ تکمیل
|
||
|
||
هفته ۳:
|
||
├─ فاز ۶: بهبود داشبورد کاربر [۱-۲ روز] ✅ تکمیل
|
||
├─ فاز ۷: بهینهسازی موبایل [۱-۲ روز] ✅ تکمیل
|
||
└─ تست + رفع باگ + دیپلوی [۱-۲ روز]
|
||
```
|
||
|
||
**مجموع تخمین: ۱۲-۲۰ روز کاری (۲.۵ تا ۴ هفته)**
|
||
|
||
### ترتیب وابستگی
|
||
|
||
```
|
||
فاز ۱ (Backend) ──► فاز ۲ (Admin Blog)
|
||
│
|
||
├──────────► فاز ۳ (Dynamic Pages)
|
||
│
|
||
├──────────► فاز ۵ (FrontOffice Blog)
|
||
│ │
|
||
│ ▼
|
||
└──────────► فاز ۴ (Landing) ──► فاز ۷ (Mobile)
|
||
▲
|
||
فاز ۶ (Dashboard) ───────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 14. ریسکها و وابستگیها
|
||
|
||
| ریسک | احتمال | تأثیر | راهحل |
|
||
|------|--------|-------|--------|
|
||
| WYSIWYG Editor محدودیت RTL | متوسط | متوسط | تست اولیه MudBlazor.HtmlEditor با فارسی، fallback به textarea + markdown |
|
||
| حجم Migration بزرگ | کم | بالا | Migration را تکهتکه اجرا کن (Blog اول، SitePage بعد) |
|
||
| عملکرد لندینگ با انیمیشنها | متوسط | متوسط | Lazy load + Intersection Observer + حداقل JS |
|
||
| FMS قطعی هنگام آپلود تصویر | کم | بالا | Retry policy + پیام خطای مناسب |
|
||
| Slug فارسی در URL | متوسط | کم | استفاده از ID-based URLs با slug فقط برای SEO |
|
||
| سازگاری Dark Mode | متوسط | کم | تست هر بخش در هر دو حالت |
|
||
|
||
---
|
||
|
||
## ضمیمه: نمونه کد الگوهای کلیدی
|
||
|
||
### A. الگوی Entity (مطابق پروژه)
|
||
|
||
```csharp
|
||
// Domain/Entities/Blog/BlogPost.cs
|
||
public class BlogPost : BaseAuditableEntity
|
||
{
|
||
public string Title { get; set; } = default!;
|
||
public string Slug { get; set; } = default!;
|
||
public string? Summary { get; set; }
|
||
public string HtmlContent { get; set; } = default!;
|
||
public string? FeaturedImagePath { get; set; }
|
||
public string? FeaturedImageThumbnailPath { get; set; }
|
||
public BlogPostStatus Status { get; set; } = BlogPostStatus.Draft;
|
||
public DateTime? PublishedAt { get; set; }
|
||
public int ViewCount { get; set; }
|
||
public long AuthorUserId { get; set; }
|
||
public bool IsFeatured { get; set; }
|
||
public int SortOrder { get; set; }
|
||
|
||
public ICollection<BlogPostCategory> BlogPostCategories { get; set; } = new List<BlogPostCategory>();
|
||
public ICollection<BlogPostTag> BlogPostTags { get; set; } = new List<BlogPostTag>();
|
||
public ICollection<BlogPostImage> BlogPostImages { get; set; } = new List<BlogPostImage>();
|
||
}
|
||
```
|
||
|
||
### B. الگوی Proto (مطابق پروژه)
|
||
|
||
```protobuf
|
||
// blogpost.proto
|
||
syntax = "proto3";
|
||
option csharp_namespace = "CMSMicroservice.Protobuf";
|
||
import "google/protobuf/timestamp.proto";
|
||
import "google/protobuf/wrappers.proto";
|
||
|
||
service BlogPostContract {
|
||
rpc CreateBlogPost (CreateBlogPostRequest) returns (BlogPostResponse);
|
||
rpc GetPublishedBlogPosts (GetPublishedBlogPostsRequest) returns (BlogPostListResponse);
|
||
// ...
|
||
}
|
||
|
||
message CreateBlogPostRequest {
|
||
string title = 1;
|
||
string slug = 2;
|
||
string summary = 3;
|
||
string html_content = 4;
|
||
bytes featured_image = 5;
|
||
string featured_image_file_name = 6;
|
||
repeated int64 category_ids = 7;
|
||
repeated int64 tag_ids = 8;
|
||
bool is_featured = 9;
|
||
}
|
||
|
||
message BlogPostResponse {
|
||
int64 id = 1;
|
||
string title = 2;
|
||
string slug = 3;
|
||
string summary = 4;
|
||
string html_content = 5;
|
||
string featured_image_path = 6;
|
||
string featured_image_thumbnail_path = 7;
|
||
int32 status = 8;
|
||
google.protobuf.Timestamp published_at = 9;
|
||
int32 view_count = 10;
|
||
bool is_featured = 11;
|
||
repeated BlogCategoryInfo categories = 12;
|
||
repeated TagInfo tags = 13;
|
||
}
|
||
```
|
||
|
||
### C. الگوی CQRS Handler (مطابق پروژه)
|
||
|
||
```csharp
|
||
// Application/BlogPostCQ/Commands/CreateBlogPost/CreateBlogPostCommandHandler.cs
|
||
public class CreateBlogPostCommandHandler : IRequestHandler<CreateBlogPostCommand, long>
|
||
{
|
||
private readonly IApplicationDbContext _context;
|
||
private readonly IFileManagerService _fileManager;
|
||
|
||
public CreateBlogPostCommandHandler(
|
||
IApplicationDbContext context,
|
||
IFileManagerService fileManager)
|
||
{
|
||
_context = context;
|
||
_fileManager = fileManager;
|
||
}
|
||
|
||
public async Task<long> Handle(CreateBlogPostCommand request, CancellationToken ct)
|
||
{
|
||
var post = new BlogPost
|
||
{
|
||
Title = request.Title,
|
||
Slug = request.Slug,
|
||
Summary = request.Summary,
|
||
HtmlContent = request.HtmlContent,
|
||
Status = BlogPostStatus.Draft,
|
||
AuthorUserId = request.AuthorUserId,
|
||
};
|
||
|
||
if (request.FeaturedImage?.Length > 0)
|
||
{
|
||
var (path, thumb) = await _fileManager.UploadFileAsync(
|
||
request.FeaturedImage, request.FeaturedImageFileName);
|
||
post.FeaturedImagePath = path;
|
||
post.FeaturedImageThumbnailPath = thumb;
|
||
}
|
||
|
||
_context.BlogPosts.Add(post);
|
||
await _context.SaveChangesAsync(ct);
|
||
|
||
// Add categories & tags...
|
||
|
||
return post.Id;
|
||
}
|
||
}
|
||
```
|
||
|
||
### D. الگوی gRPC Service (مطابق پروژه)
|
||
|
||
```csharp
|
||
// WebApi/Services/BlogPostService.cs
|
||
public class BlogPostService : BlogPostContract.BlogPostContractBase
|
||
{
|
||
private readonly IMediator _mediator;
|
||
private readonly IMapper _mapper;
|
||
|
||
public BlogPostService(IMediator mediator, IMapper mapper)
|
||
{
|
||
_mediator = mediator;
|
||
_mapper = mapper;
|
||
}
|
||
|
||
public override async Task<BlogPostResponse> CreateBlogPost(
|
||
CreateBlogPostRequest request, ServerCallContext context)
|
||
{
|
||
var command = _mapper.Map<CreateBlogPostCommand>(request);
|
||
var id = await _mediator.Send(command);
|
||
// Get and return the created post...
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
> **یادداشت:** این سند یک نقشهراه جامع است. هر فاز به صورت مستقل قابل پیادهسازی است و وابستگیها در بخش ۱۳ مشخص شدهاند. بعد از تأیید این طرح، پیادهسازی فاز به فاز شروع میشود.
|