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;
}