Files
social-media/backend/src/Socialize.Api/Modules/CalendarIntegrations/Services/CalendarExportFeedService.cs
Jonathan Bourdon b66c10b681
Some checks failed
Backend CI/CD / build_and_deploy (push) Has been cancelled
Frontend CI/CD / build_and_deploy (push) Has been cancelled
Add calendar integrations and collaboration updates
2026-05-05 15:25:53 -04:00

174 lines
6.7 KiB
C#

using Microsoft.EntityFrameworkCore;
using Socialize.Api.Data;
namespace Socialize.Api.Modules.CalendarIntegrations.Services;
public class CalendarExportFeedService(AppDbContext dbContext, CalendarExportFeedBuilder feedBuilder)
{
public async Task<string> BuildUserFeedAsync(Guid userId, string? userEmail, string appBaseUrl, CancellationToken ct)
{
string normalizedEmail = userEmail?.Trim().ToUpperInvariant() ?? string.Empty;
Guid[] workspaceIds = await dbContext.Workspaces
.Where(workspace =>
workspace.OwnerUserId == userId ||
dbContext.OrganizationMemberships.Any(membership =>
membership.OrganizationId == workspace.OrganizationId &&
membership.UserId == userId))
.Select(workspace => workspace.Id)
.ToArrayAsync(ct);
List<CalendarExportFeedEvent> events = [];
events.AddRange(await dbContext.ContentItems
.Where(item => workspaceIds.Contains(item.WorkspaceId) && item.DueDate.HasValue)
.Join(
dbContext.Workspaces,
item => item.WorkspaceId,
workspace => workspace.Id,
(item, workspace) => new { item, workspace })
.Join(
dbContext.Clients,
itemWorkspace => itemWorkspace.item.ClientId,
client => client.Id,
(itemWorkspace, client) => new { itemWorkspace.item, itemWorkspace.workspace, client })
.Join(
dbContext.Campaigns,
itemWorkspaceClient => itemWorkspaceClient.item.CampaignId,
campaign => campaign.Id,
(itemWorkspaceClient, campaign) => new { itemWorkspaceClient.item, itemWorkspaceClient.workspace, itemWorkspaceClient.client, campaign })
.Select(candidate => ToContentFeedEvent(
candidate.item.Id,
candidate.item.Title,
candidate.item.Status,
candidate.item.DueDate!.Value,
candidate.workspace.Name,
candidate.client.Name,
candidate.campaign.Name,
appBaseUrl))
.ToListAsync(ct));
events.AddRange(await dbContext.ApprovalRequests
.Where(approval =>
approval.DueAt.HasValue &&
(approval.RequestedByUserId == userId ||
(!string.IsNullOrEmpty(normalizedEmail) && approval.ReviewerEmail.ToUpper() == normalizedEmail)))
.Join(
dbContext.ContentItems,
approval => approval.ContentItemId,
item => item.Id,
(approval, item) => new { approval, item })
.Where(candidate => workspaceIds.Contains(candidate.approval.WorkspaceId))
.Join(
dbContext.Workspaces,
approvalItem => approvalItem.approval.WorkspaceId,
workspace => workspace.Id,
(approvalItem, workspace) => new { approvalItem.approval, approvalItem.item, workspace })
.Select(candidate => ToApprovalFeedEvent(
candidate.approval.Id,
candidate.item.Id,
candidate.item.Title,
candidate.approval.Stage,
candidate.approval.State,
candidate.approval.DueAt!.Value,
candidate.workspace.Name,
appBaseUrl))
.ToListAsync(ct));
events.AddRange(await dbContext.Campaigns
.Where(campaign => workspaceIds.Contains(campaign.WorkspaceId))
.Join(
dbContext.Workspaces,
campaign => campaign.WorkspaceId,
workspace => workspace.Id,
(campaign, workspace) => new { campaign, workspace })
.Select(candidate => ToCampaignFeedEvent(
candidate.campaign.Id,
candidate.campaign.Name,
candidate.campaign.Status,
candidate.campaign.StartDate,
candidate.campaign.EndDate,
candidate.workspace.Name,
appBaseUrl))
.ToListAsync(ct));
return feedBuilder.Build("Socialize my work", events);
}
private static CalendarExportFeedEvent ToContentFeedEvent(
Guid contentItemId,
string title,
string status,
DateTimeOffset dueDate,
string workspaceName,
string clientName,
string campaignName,
string appBaseUrl)
{
(DateTimeOffset start, DateTimeOffset end, bool isAllDay) = NormalizeEventTime(dueDate);
return new CalendarExportFeedEvent(
$"content-{contentItemId}@socialize",
title,
start,
end,
isAllDay,
$"Status: {status}\nWorkspace: {workspaceName}\nClient: {clientName}\nCampaign: {campaignName}",
$"{appBaseUrl.TrimEnd('/')}/app/content/{contentItemId}");
}
private static CalendarExportFeedEvent ToApprovalFeedEvent(
Guid approvalId,
Guid contentItemId,
string contentTitle,
string stage,
string state,
DateTimeOffset dueAt,
string workspaceName,
string appBaseUrl)
{
(DateTimeOffset start, DateTimeOffset end, bool isAllDay) = NormalizeEventTime(dueAt);
return new CalendarExportFeedEvent(
$"approval-{approvalId}@socialize",
$"Approval due: {contentTitle}",
start,
end,
isAllDay,
$"Stage: {stage}\nState: {state}\nWorkspace: {workspaceName}",
$"{appBaseUrl.TrimEnd('/')}/app/content/{contentItemId}");
}
private static CalendarExportFeedEvent ToCampaignFeedEvent(
Guid campaignId,
string name,
string status,
DateTimeOffset startDate,
DateTimeOffset endDate,
string workspaceName,
string appBaseUrl)
{
DateTimeOffset start = new(startDate.Date, startDate.Offset);
DateTimeOffset end = new(endDate.Date.AddDays(1), endDate.Offset);
return new CalendarExportFeedEvent(
$"campaign-{campaignId}@socialize",
$"Campaign: {name}",
start,
end <= start ? start.AddDays(1) : end,
true,
$"Status: {status}\nWorkspace: {workspaceName}",
$"{appBaseUrl.TrimEnd('/')}/app/campaigns/{campaignId}");
}
private static (DateTimeOffset Start, DateTimeOffset End, bool IsAllDay) NormalizeEventTime(DateTimeOffset value)
{
if (value.TimeOfDay == TimeSpan.Zero)
{
DateTimeOffset start = new(value.Date, value.Offset);
return (start, start.AddDays(1), true);
}
return (value, value.AddMinutes(30), false);
}
}