namespace FrontOffice.Main.Utilities; /// /// Service for fetching published blog posts (customer-facing). /// Used by Landing Page (latest posts) and future Blog pages. /// public class BlogPostService { private readonly CMSMicroservice.Protobuf.Protos.BlogPost.BlogPostContract.BlogPostContractClient _client; public BlogPostService(CMSMicroservice.Protobuf.Protos.BlogPost.BlogPostContract.BlogPostContractClient client) { _client = client; } /// /// Get featured blog posts (for landing page hero / highlights). /// public async Task> GetFeaturedPostsAsync(int count = 3) { try { var response = await _client.GetFeaturedBlogPostsAsync( new CMSMicroservice.Protobuf.Protos.BlogPost.GetFeaturedBlogPostsRequest { Count = count }); return MapToCardDtos(response?.Models); } catch (Exception ex) { #if DEBUG Console.WriteLine($"BlogPostService.GetFeaturedPostsAsync error: {ex.Message}"); #endif return new(); } } /// /// Get latest published posts with optional category filter. /// public async Task GetPublishedPostsAsync(int page = 1, int pageSize = 9, string? search = null, long? categoryId = null) { try { var request = new CMSMicroservice.Protobuf.Protos.BlogPost.GetPublishedBlogPostsRequest { PaginationState = new CMSMicroservice.Protobuf.Protos.PaginationState { PageNumber = page, PageSize = pageSize } }; if (!string.IsNullOrWhiteSpace(search)) request.SearchTerm = search; if (categoryId.HasValue) request.CategoryId = categoryId.Value; var response = await _client.GetPublishedBlogPostsAsync(request); return new BlogPostListResult { Posts = MapToCardDtos(response?.Models), TotalCount = (int)(response?.MetaData?.TotalCount ?? 0), TotalPages = (int)(response?.MetaData?.TotalPage ?? 0), CurrentPage = page }; } catch (Exception ex) { #if DEBUG Console.WriteLine($"BlogPostService.GetPublishedPostsAsync error: {ex.Message}"); #endif return new(); } } /// /// Get a single blog post by slug (for blog detail page). /// public async Task GetBySlugAsync(string slug) { try { var response = await _client.GetBlogPostBySlugAsync( new CMSMicroservice.Protobuf.Protos.BlogPost.GetBlogPostBySlugRequest { Slug = slug }); if (response == null || response.Id <= 0) return null; return new BlogPostDetailDto { Id = response.Id, Title = response.Title, Slug = response.Slug, Summary = response.Summary, HtmlContent = response.HtmlContent, FeaturedImagePath = response.FeaturedImagePath, FeaturedImageThumbnailPath = response.FeaturedImageThumbnailPath, PublishedAt = response.PublishedAt?.ToDateTime(), ViewCount = response.ViewCount, IsFeatured = response.IsFeatured, Categories = response.Categories?.Select(c => new BlogCategoryInfo { Id = c.Id, Title = c.Title, Slug = c.Slug }).ToList() ?? new(), Tags = response.Tags?.Select(t => new BlogTagInfo { Id = t.Id, Title = t.Title, Name = t.Name }).ToList() ?? new() }; } catch (Exception ex) { #if DEBUG Console.WriteLine($"BlogPostService.GetBySlugAsync error: {ex.Message}"); #endif return null; } } /// /// Increment view count for a blog post. /// public async Task IncrementViewCountAsync(long postId) { try { await _client.IncrementViewCountAsync( new CMSMicroservice.Protobuf.Protos.BlogPost.IncrementViewCountRequest { Id = postId }); } catch { // Fire and forget — don't fail the page } } // ── Helpers ── private static List MapToCardDtos( Google.Protobuf.Collections.RepeatedField? models) { if (models == null || models.Count == 0) return new(); return models.Select(m => new BlogPostCardDto { Id = m.Id, Title = m.Title, Slug = m.Slug, Summary = m.Summary, ThumbnailUrl = m.FeaturedImageThumbnailPath, PublishedAt = m.PublishedAt?.ToDateTime(), ViewCount = m.ViewCount, IsFeatured = m.IsFeatured, Categories = m.Categories?.Select(c => new BlogCategoryInfo { Id = c.Id, Title = c.Title, Slug = c.Slug }).ToList() ?? new() }).ToList(); } } // ── DTOs ── public class BlogPostCardDto { public long Id { get; set; } public string Title { get; set; } = string.Empty; public string Slug { get; set; } = string.Empty; public string? Summary { get; set; } public string? ThumbnailUrl { get; set; } public DateTime? PublishedAt { get; set; } public int ViewCount { get; set; } public bool IsFeatured { get; set; } public List Categories { get; set; } = new(); } public class BlogPostDetailDto { public long Id { get; set; } public string Title { get; set; } = string.Empty; public string Slug { get; set; } = string.Empty; public string? Summary { get; set; } public string HtmlContent { get; set; } = string.Empty; public string? FeaturedImagePath { get; set; } public string? FeaturedImageThumbnailPath { get; set; } public DateTime? PublishedAt { get; set; } public int ViewCount { get; set; } public bool IsFeatured { get; set; } public List Categories { get; set; } = new(); public List Tags { get; set; } = new(); } public class BlogPostListResult { public List Posts { get; set; } = new(); public int TotalCount { get; set; } public int TotalPages { get; set; } public int CurrentPage { get; set; } } public class BlogCategoryInfo { public long Id { get; set; } public string Title { get; set; } = string.Empty; public string Slug { get; set; } = string.Empty; } public class BlogTagInfo { public long Id { get; set; } public string Title { get; set; } = string.Empty; public string Name { get; set; } = string.Empty; }