Compare commits

...

27 Commits

Author SHA1 Message Date
49b85c4df9 Rresloving rebase issues 2025-07-08 12:38:00 +05:30
aebb344a5a Implemented the cache in task allocation 2025-07-08 12:36:17 +05:30
56aca323e5 Storing workItem in cache and changing planned work and completed work for respective project, building, floor, and workarea 2025-07-08 12:22:29 +05:30
1d318c75d8 Implemented the methods for deleting permission am asigned project from caches for certien employee 2025-07-08 12:22:29 +05:30
8521a68c3e Added error handling in cache helper 2025-07-08 12:22:28 +05:30
8c85d92ba6 removed comented code from appsetting file 2025-07-08 12:22:28 +05:30
3ce9851a7f Saving project details with infrastructure, employee permissions and assigned project for that employee in mongodb 2025-07-08 12:22:28 +05:30
3dd5e7f626 project details API is split into three APIs. 2025-07-08 12:22:28 +05:30
bc0ef0b88b Merge pull request 'Image_Gallery' (#101) from Image_Gallery into main
Reviewed-on: #101
2025-07-08 06:42:02 +00:00
43e2aeb097 Showing the comment added when task is reported 2025-07-07 13:15:27 +05:30
65f3376523 Added new paremeter of NumberOfImages in signalR message object 2025-07-07 11:21:31 +05:30
558fd6bd5b Corrected Key for signalR 2025-07-05 17:24:57 +05:30
e4cc6c1578 Merge pull request 'Added SignalR Integration for Reporting, Commenting, and Approving Tasks' (#100) from Ashutosh_Task#663 into Image_Gallery
Reviewed-on: #100
2025-07-05 11:44:00 +00:00
6373da3144 Added SignalR Integration for Reporting, Commenting, and Approving Tasks 2025-07-05 17:11:59 +05:30
f9ab7bb3c8 Ordered by Uploaded at 2025-07-05 13:19:06 +05:30
800db99fd9 Removed double Deserialization 2025-07-05 12:33:10 +05:30
6d8939d942 Added Pagenation to Image List API 2025-07-05 12:23:28 +05:30
c9ff53a7ac Removed double Deserialize 2025-07-03 12:46:49 +05:30
a303625d59 Added a line which is missed while optimizing 2025-07-03 11:32:06 +05:30
62eb914456 Added filter in get image list API 2025-07-02 17:42:42 +05:30
1f5a71ef09 Added WorkCategoryName and WorkCategoryId Parameters in view models 2025-07-02 13:15:28 +05:30
8353c384a5 Added athorization in controller 2025-07-02 10:15:00 +05:30
85911c4536 Added an API to get persigned url for provided document 2025-07-02 10:04:00 +05:30
afdf51eae3 Added an API to get list of Images for provided batch ID 2025-07-02 10:03:20 +05:30
587e8d2b0b Added an API to get list of images in provided project. 2025-07-02 10:02:22 +05:30
b77a5b16cd Storing batch ID when saving any images as well stop storing base64 in database 2025-07-02 10:01:10 +05:30
ba1e644fd8 Added new parameter uploaded by in documents table 2025-07-02 09:59:56 +05:30
11 changed files with 4027 additions and 40 deletions

View File

@ -0,0 +1,50 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_UploadedBy_ForeginKey_In_Decuments_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "UploadedById",
table: "Documents",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.CreateIndex(
name: "IX_Documents_UploadedById",
table: "Documents",
column: "UploadedById");
migrationBuilder.AddForeignKey(
name: "FK_Documents_Employees_UploadedById",
table: "Documents",
column: "UploadedById",
principalTable: "Employees",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Documents_Employees_UploadedById",
table: "Documents");
migrationBuilder.DropIndex(
name: "IX_Documents_UploadedById",
table: "Documents");
migrationBuilder.DropColumn(
name: "UploadedById",
table: "Documents");
}
}
}

View File

@ -752,10 +752,15 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<DateTime>("UploadedAt") b.Property<DateTime>("UploadedAt")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<Guid?>("UploadedById")
.HasColumnType("char(36)");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("TenantId"); b.HasIndex("TenantId");
b.HasIndex("UploadedById");
b.ToTable("Documents"); b.ToTable("Documents");
}); });
@ -2951,7 +2956,13 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "UploadedBy")
.WithMany()
.HasForeignKey("UploadedById");
b.Navigation("Tenant"); b.Navigation("Tenant");
b.Navigation("UploadedBy");
}); });
modelBuilder.Entity("Marco.Pms.Model.Employees.Employee", b => modelBuilder.Entity("Marco.Pms.Model.Employees.Employee", b =>

View File

@ -1,4 +1,7 @@
using Marco.Pms.Model.Utilities; using System.ComponentModel.DataAnnotations.Schema;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
namespace Marco.Pms.Model.DocumentManager namespace Marco.Pms.Model.DocumentManager
{ {
@ -16,10 +19,15 @@ namespace Marco.Pms.Model.DocumentManager
/// </summary> /// </summary>
public string? ThumbS3Key { get; set; } public string? ThumbS3Key { get; set; }
public string? Base64Data { get; set; } public string? Base64Data { get; set; } = null;
public long FileSize { get; set; } public long FileSize { get; set; }
public string ContentType { get; set; } = string.Empty; public string ContentType { get; set; } = string.Empty;
public Guid? UploadedById { get; set; }
[ValidateNever]
[ForeignKey("UploadedById")]
public Employee? UploadedBy { get; set; }
public DateTime UploadedAt { get; set; } public DateTime UploadedAt { get; set; }
} }
} }

View File

@ -0,0 +1,10 @@
using Marco.Pms.Model.DocumentManager;
namespace Marco.Pms.Model.Dtos.DocumentManager
{
public class DocumentBatchDto
{
public Guid? BatchId { get; set; }
public List<Document>? Documents { get; set; }
}
}

View File

@ -90,29 +90,35 @@ namespace Marco.Pms.Model.Mapper
}; };
} }
public static Document ToDocumentFromForumAttachmentDto(this ForumAttachmentDto AttachmentDto, string objectKey, string thumbS3Key, DateTime uploadedAt, Guid tenantId) public static Document ToDocumentFromForumAttachmentDto(this ForumAttachmentDto AttachmentDto, string objectKey, string thumbS3Key, DateTime uploadedAt,
Guid tenantId, Guid batchId, Guid loggedInEmployeeId)
{ {
return new Document return new Document
{ {
BatchId = batchId,
UploadedById = loggedInEmployeeId,
FileName = AttachmentDto.FileName, FileName = AttachmentDto.FileName,
ContentType = AttachmentDto.ContentType, ContentType = AttachmentDto.ContentType,
S3Key = objectKey, S3Key = objectKey,
ThumbS3Key = thumbS3Key, ThumbS3Key = thumbS3Key,
Base64Data = AttachmentDto.Base64Data, //Base64Data = AttachmentDto.Base64Data,
FileSize = AttachmentDto.FileSize, FileSize = AttachmentDto.FileSize,
UploadedAt = uploadedAt, UploadedAt = uploadedAt,
TenantId = tenantId TenantId = tenantId
}; };
} }
public static Document ToDocumentFromUpdateAttachmentDto(this UpdateAttachmentDto AttachmentDto, string objectKey, string thumbS3Key, DateTime uploadedAt, Guid tenantId) public static Document ToDocumentFromUpdateAttachmentDto(this UpdateAttachmentDto AttachmentDto, string objectKey, string thumbS3Key, DateTime uploadedAt,
Guid tenantId, Guid batchId, Guid loggedInEmployeeId)
{ {
return new Document return new Document
{ {
BatchId = batchId,
UploadedById = loggedInEmployeeId,
FileName = AttachmentDto.FileName, FileName = AttachmentDto.FileName,
ContentType = AttachmentDto.ContentType, ContentType = AttachmentDto.ContentType,
S3Key = objectKey, S3Key = objectKey,
ThumbS3Key = thumbS3Key, ThumbS3Key = thumbS3Key,
Base64Data = AttachmentDto.Base64Data, //Base64Data = AttachmentDto.Base64Data,
FileSize = AttachmentDto.FileSize, FileSize = AttachmentDto.FileSize,
UploadedAt = uploadedAt, UploadedAt = uploadedAt,
TenantId = tenantId TenantId = tenantId

View File

@ -0,0 +1,14 @@
namespace Marco.Pms.Model.Utilities
{
public class ImageFilter
{
public List<Guid>? BuildingIds { get; set; }
public List<Guid>? FloorIds { get; set; }
public List<Guid>? WorkAreaIds { get; set; }
public List<Guid>? WorkCategoryIds { get; set; }
public List<Guid>? ActivityIds { get; set; }
public List<Guid>? UploadedByIds { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
}

View File

@ -603,7 +603,8 @@ namespace MarcoBMS.Services.Controllers
} }
Guid tenantId = GetTenantId(); Guid tenantId = GetTenantId();
var currentEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var batchId = Guid.NewGuid();
using var transaction = await _context.Database.BeginTransactionAsync(); using var transaction = await _context.Database.BeginTransactionAsync();
try try
@ -704,10 +705,12 @@ namespace MarcoBMS.Services.Controllers
document = new Document document = new Document
{ {
BatchId = batchId,
UploadedById = loggedInEmployee.Id,
FileName = recordAttendanceDot.Image.FileName ?? "", FileName = recordAttendanceDot.Image.FileName ?? "",
ContentType = recordAttendanceDot.Image.ContentType, ContentType = recordAttendanceDot.Image.ContentType,
S3Key = objectKey, S3Key = objectKey,
Base64Data = recordAttendanceDot.Image.Base64Data, //Base64Data = recordAttendanceDot.Image.Base64Data,
FileSize = recordAttendanceDot.Image.FileSize, FileSize = recordAttendanceDot.Image.FileSize,
UploadedAt = recordAttendanceDot.Date, UploadedAt = recordAttendanceDot.Date,
TenantId = tenantId TenantId = tenantId
@ -728,7 +731,7 @@ namespace MarcoBMS.Services.Controllers
Longitude = recordAttendanceDot.Longitude, Longitude = recordAttendanceDot.Longitude,
DocumentId = document?.Id, DocumentId = document?.Id,
TenantId = tenantId, TenantId = tenantId,
UpdatedBy = recordAttendanceDot.EmployeeID, UpdatedBy = loggedInEmployee.Id,
UpdatedOn = recordAttendanceDot.Date UpdatedOn = recordAttendanceDot.Date
}; };
_context.AttendanceLogs.Add(attendanceLog); _context.AttendanceLogs.Add(attendanceLog);
@ -755,7 +758,7 @@ namespace MarcoBMS.Services.Controllers
var notification = new var notification = new
{ {
LoggedInUserId = currentEmployee.Id, LoggedInUserId = loggedInEmployee.Id,
Keyword = "Attendance", Keyword = "Attendance",
Activity = recordAttendanceDot.Id == Guid.Empty ? 1 : 0, Activity = recordAttendanceDot.Id == Guid.Empty ? 1 : 0,
ProjectId = attendance.ProjectID, ProjectId = attendance.ProjectID,

View File

@ -48,6 +48,8 @@ namespace Marco.Pms.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var batchId = Guid.NewGuid();
TicketForum ticketForum = createTicketDto.ToTicketForumFromCreateTicketDto(tenantId); TicketForum ticketForum = createTicketDto.ToTicketForumFromCreateTicketDto(tenantId);
_context.Tickets.Add(ticketForum); _context.Tickets.Add(ticketForum);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@ -79,7 +81,7 @@ namespace Marco.Pms.Services.Controllers
string objectKey = $"tenant-{tenantId}/project-{createTicketDto.LinkedProjectId}/froum/{fileName}"; string objectKey = $"tenant-{tenantId}/project-{createTicketDto.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey); await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, createTicketDto.CreatedAt, tenantId); Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, createTicketDto.CreatedAt, tenantId, batchId, loggedInEmployee.Id);
_context.Documents.Add(document); _context.Documents.Add(document);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@ -162,7 +164,15 @@ namespace Marco.Pms.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
var existingTicket = await _context.Tickets.Include(t => t.TicketTypeMaster).Include(t => t.TicketStatusMaster).Include(t => t.Priority).AsNoTracking().FirstOrDefaultAsync(t => t.Id == updateTicketDto.Id); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var batchId = Guid.NewGuid();
var existingTicket = await _context.Tickets
.Include(t => t.TicketTypeMaster)
.Include(t => t.TicketStatusMaster)
.Include(t => t.Priority)
.AsNoTracking()
.FirstOrDefaultAsync(t => t.Id == updateTicketDto.Id);
if (existingTicket != null) if (existingTicket != null)
{ {
TicketForum ticketForum = updateTicketDto.ToTicketForumFromUpdateTicketDto(existingTicket); TicketForum ticketForum = updateTicketDto.ToTicketForumFromUpdateTicketDto(existingTicket);
@ -202,7 +212,7 @@ namespace Marco.Pms.Services.Controllers
string objectKey = $"tenant-{tenantId}/project-{updateTicketDto.LinkedProjectId}/froum/{fileName}"; string objectKey = $"tenant-{tenantId}/project-{updateTicketDto.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey); await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, updateTicketDto.CreatedAt, tenantId); Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, updateTicketDto.CreatedAt, tenantId, batchId, loggedInEmployee.Id);
_context.Documents.Add(document); _context.Documents.Add(document);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@ -344,6 +354,9 @@ namespace Marco.Pms.Services.Controllers
} }
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var batchId = Guid.NewGuid();
List<TicketAttachment> attachments = new List<TicketAttachment>(); List<TicketAttachment> attachments = new List<TicketAttachment>();
List<Document> documents = new List<Document>(); List<Document> documents = new List<Document>();
@ -381,7 +394,7 @@ namespace Marco.Pms.Services.Controllers
string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}"; string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey); await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, addCommentDto.SentAt, tenantId); Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, addCommentDto.SentAt, tenantId, batchId, loggedInEmployee.Id);
_context.Documents.Add(document); _context.Documents.Add(document);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@ -429,6 +442,9 @@ namespace Marco.Pms.Services.Controllers
} }
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var batchId = Guid.NewGuid();
List<TicketAttachment> attachments = new List<TicketAttachment>(); List<TicketAttachment> attachments = new List<TicketAttachment>();
TicketForum? ticket = await _context.Tickets.FirstOrDefaultAsync(t => t.Id == updateCommentDto.TicketId); TicketForum? ticket = await _context.Tickets.FirstOrDefaultAsync(t => t.Id == updateCommentDto.TicketId);
@ -473,7 +489,7 @@ namespace Marco.Pms.Services.Controllers
string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}"; string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey); await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, existingComment.SentAt, tenantId); Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, existingComment.SentAt, tenantId, batchId, loggedInEmployee.Id);
_context.Documents.Add(document); _context.Documents.Add(document);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@ -541,6 +557,9 @@ namespace Marco.Pms.Services.Controllers
} }
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var batchId = Guid.NewGuid();
List<TicketAttachmentVM> ticketAttachmentVMs = new List<TicketAttachmentVM>(); List<TicketAttachmentVM> ticketAttachmentVMs = new List<TicketAttachmentVM>();
List<Guid> ticketIds = forumAttachmentDtos.Select(f => f.TicketId.HasValue ? f.TicketId.Value : Guid.Empty).ToList(); List<Guid> ticketIds = forumAttachmentDtos.Select(f => f.TicketId.HasValue ? f.TicketId.Value : Guid.Empty).ToList();
@ -579,7 +598,7 @@ namespace Marco.Pms.Services.Controllers
string objectKey = $"tenant-{tenantId}/project-{ticket?.LinkedProjectId}/froum/{fileName}"; string objectKey = $"tenant-{tenantId}/project-{ticket?.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey); await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = forumAttachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, forumAttachmentDto.SentAt, tenantId); Document document = forumAttachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, forumAttachmentDto.SentAt, tenantId, batchId, loggedInEmployee.Id);
_context.Documents.Add(document); _context.Documents.Add(document);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();

View File

@ -0,0 +1,404 @@
using System.Text.Json;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Activities;
using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
namespace Marco.Pms.Services.Controllers
{
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ImageController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly S3UploadService _s3Service;
private readonly UserHelper _userHelper;
private readonly ILoggingService _logger;
private readonly PermissionServices _permission;
private readonly Guid tenantId;
public ImageController(ApplicationDbContext context, S3UploadService s3Service, UserHelper userHelper, ILoggingService logger, PermissionServices permission)
{
_context = context;
_s3Service = s3Service;
_userHelper = userHelper;
_logger = logger;
tenantId = userHelper.GetTenantId();
_permission = permission;
}
[HttpGet("images/{projectId}")]
public async Task<IActionResult> GetImageList(Guid projectId, [FromQuery] string? filter, [FromQuery] int? pageNumber = 1, [FromQuery] int? pageSize = 10)
{
_logger.LogInfo("[GetImageList] Called by Employee for ProjectId: {ProjectId}", projectId);
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// Step 1: Validate project existence
var isProjectExist = await _context.Projects.AnyAsync(p => p.Id == projectId && p.TenantId == tenantId);
if (!isProjectExist)
{
_logger.LogWarning("[GetImageList] ProjectId: {ProjectId} not found", projectId);
return BadRequest(ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 400));
}
// Step 2: Check project access permission
var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId.ToString());
if (!hasPermission)
{
_logger.LogWarning("[GetImageList] Access denied for EmployeeId: {EmployeeId} on ProjectId: {ProjectId}", loggedInEmployee.Id, projectId);
return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "You don't have access", 403));
}
// Step 3: Deserialize filter
ImageFilter? imageFilter = null;
if (!string.IsNullOrWhiteSpace(filter))
{
try
{
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
//string unescapedJsonString = JsonSerializer.Deserialize<string>(filter, options) ?? "";
//imageFilter = JsonSerializer.Deserialize<ImageFilter>(unescapedJsonString, options);
imageFilter = JsonSerializer.Deserialize<ImageFilter>(filter, options);
}
catch (Exception ex)
{
_logger.LogWarning("[GetImageList] Failed to parse filter: {Message}", ex.Message);
}
}
// Step 4: Extract filter values
var buildingIds = imageFilter?.BuildingIds;
var floorIds = imageFilter?.FloorIds;
var workAreaIds = imageFilter?.WorkAreaIds;
var activityIds = imageFilter?.ActivityIds;
var workCategoryIds = imageFilter?.WorkCategoryIds;
var startDate = imageFilter?.StartDate;
var endDate = imageFilter?.EndDate;
var uploadedByIds = imageFilter?.UploadedByIds;
// Step 5: Fetch building > floor > area > work item hierarchy
List<Building>? buildings = null;
List<Floor>? floors = null;
List<WorkArea>? workAreas = null;
if (buildingIds != null && buildingIds.Count > 0)
{
buildings = await _context.Buildings
.Where(b => b.ProjectId == projectId && buildingIds.Contains(b.Id))
.ToListAsync();
}
else
{
buildings = await _context.Buildings
.Where(b => b.ProjectId == projectId)
.ToListAsync();
buildingIds = buildings.Select(b => b.Id).ToList();
}
if (floorIds != null && floorIds.Count > 0)
{
floors = await _context.Floor
.Where(f => buildingIds.Contains(f.BuildingId) && floorIds.Contains(f.Id))
.ToListAsync();
}
else
{
floors = await _context.Floor
.Where(f => buildingIds.Contains(f.BuildingId))
.ToListAsync();
floorIds = floors.Select(f => f.Id).ToList();
}
if (workAreaIds != null && workAreaIds.Count > 0)
{
workAreas = await _context.WorkAreas
.Where(wa => floorIds.Contains(wa.FloorId) && workAreaIds.Contains(wa.Id))
.ToListAsync();
}
else
{
workAreas = await _context.WorkAreas
.Where(wa => floorIds.Contains(wa.FloorId))
.ToListAsync();
workAreaIds = workAreas.Select(wa => wa.Id).ToList();
}
var workItemsQuery = _context.WorkItems.Include(w => w.ActivityMaster).Include(w => w.WorkCategoryMaster)
.Where(wi => workAreaIds.Contains(wi.WorkAreaId));
if (activityIds?.Any() == true) workItemsQuery = workItemsQuery.Where(wi => activityIds.Contains(wi.ActivityId));
if (workCategoryIds?.Any() == true)
{
workItemsQuery = workItemsQuery.Where(wi => wi.WorkCategoryMaster != null && workCategoryIds.Contains(wi.WorkCategoryMaster.Id));
}
var workItems = await workItemsQuery.ToListAsync();
var workItemIds = workItems.Select(wi => wi.Id).ToList();
// Step 6: Fetch task allocations and comments
var tasks = await _context.TaskAllocations.Include(t => t.ReportedBy)
.Where(t => workItemIds.Contains(t.WorkItemId)).ToListAsync();
var taskIds = tasks.Select(t => t.Id).ToList();
var comments = await _context.TaskComments.Include(c => c.Employee)
.Where(c => taskIds.Contains(c.TaskAllocationId)).ToListAsync();
var commentIds = comments.Select(c => c.Id).ToList();
var attachments = await _context.TaskAttachments
.Where(ta => taskIds.Contains(ta.ReferenceId) || commentIds.Contains(ta.ReferenceId)).ToListAsync();
var documentIds = attachments.Select(ta => ta.DocumentId).ToList();
// Step 7: Fetch and filter documents
List<DocumentBatchDto> documents = new List<DocumentBatchDto>();
var docQuery = _context.Documents.Include(d => d.UploadedBy)
.Where(d => documentIds.Contains(d.Id) && d.TenantId == tenantId);
if (startDate != null && endDate != null)
{
docQuery = docQuery.Where(d => d.UploadedAt.Date >= startDate.Value.Date && d.UploadedAt.Date <= endDate.Value.Date);
}
if (pageNumber != null && pageSize != null)
{
documents = await docQuery
.GroupBy(d => d.BatchId)
.OrderByDescending(g => g.Max(d => d.UploadedAt))
.Skip((pageNumber.Value - 1) * pageSize.Value)
.Take(pageSize.Value)
.Select(g => new DocumentBatchDto
{
BatchId = g.Key,
Documents = g.ToList()
})
.ToListAsync();
Console.Write("Pagenation Success");
}
// Step 8: Build response
var documentVM = documents.Select(d =>
{
var docIds = d.Documents?.Select(x => x.Id).ToList() ?? new List<Guid>();
var refId = attachments.FirstOrDefault(ta => docIds.Contains(ta.DocumentId))?.ReferenceId;
var task = tasks.FirstOrDefault(t => t.Id == refId);
var comment = comments.FirstOrDefault(c => c.Id == refId);
var source = task != null ? "Report" : comment != null ? "Comment" : "";
var uploadedBy = task?.ReportedBy ?? comment?.Employee;
if (comment != null)
{
task = tasks.FirstOrDefault(t => t.Id == comment.TaskAllocationId);
}
if (task != null)
{
comment = comments.OrderBy(c => c.CommentDate).FirstOrDefault(c => c.TaskAllocationId == task.Id);
}
var workItem = workItems.FirstOrDefault(w => w.Id == task?.WorkItemId);
var workArea = workAreas.FirstOrDefault(wa => wa.Id == workItem?.WorkAreaId);
var floor = floors.FirstOrDefault(f => f.Id == workArea?.FloorId);
var building = buildings.FirstOrDefault(b => b.Id == floor?.BuildingId);
return new
{
BatchId = d.BatchId,
Documents = d.Documents?.Select(x => new
{
Id = x.Id,
thumbnailUrl = x.ThumbS3Key != null ? _s3Service.GeneratePreSignedUrlAsync(x.ThumbS3Key) : (x.S3Key != null ? _s3Service.GeneratePreSignedUrlAsync(x.S3Key) : null),
Url = x.S3Key != null ? _s3Service.GeneratePreSignedUrlAsync(x.S3Key) : null,
UploadedBy = x.UploadedBy?.ToBasicEmployeeVMFromEmployee() ?? uploadedBy?.ToBasicEmployeeVMFromEmployee(),
UploadedAt = x.UploadedAt,
}).ToList(),
Source = source,
ProjectId = projectId,
BuildingId = building?.Id,
BuildingName = building?.Name,
FloorIds = floor?.Id,
FloorName = floor?.FloorName,
WorkAreaId = workArea?.Id,
WorkAreaName = workArea?.AreaName,
TaskId = task?.Id,
ActivityId = workItem?.ActivityMaster?.Id,
ActivityName = workItem?.ActivityMaster?.ActivityName,
WorkCategoryId = workItem?.WorkCategoryMaster?.Id,
WorkCategoryName = workItem?.WorkCategoryMaster?.Name,
CommentId = comment?.Id,
Comment = comment?.Comment
};
}).ToList();
if (uploadedByIds?.Any() == true)
{
documentVM = documentVM.Where(d => d.Documents != null && d.Documents.Any(x => uploadedByIds.Contains(x.UploadedBy?.Id ?? Guid.Empty))).ToList();
}
_logger.LogInfo("[GetImageList] Fetched {Count} documents for ProjectId: {ProjectId}", documentVM.Count, projectId);
return Ok(ApiResponse<object>.SuccessResponse(documentVM, $"{documentVM.Count} image records fetched successfully", 200));
}
[HttpGet("batch/{batchId}")]
public async Task<IActionResult> GetImagesByBatch(Guid batchId)
{
_logger.LogInfo("GetImagesByBatch called for BatchId: {BatchId}", batchId);
// Step 1: Get the logged-in employee
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// Step 2: Retrieve all documents in the batch
var documents = await _context.Documents
.Include(d => d.UploadedBy)
.Where(d => d.BatchId == batchId)
.ToListAsync();
if (!documents.Any())
{
_logger.LogWarning("No documents found for BatchId: {BatchId}", batchId);
return NotFound(ApiResponse<object>.ErrorResponse("No images found", "No images associated with this batch", 404));
}
var documentIds = documents.Select(d => d.Id).ToList();
// Step 3: Get task/comment reference IDs linked to these documents
var referenceIds = await _context.TaskAttachments
.Where(ta => documentIds.Contains(ta.DocumentId))
.Select(ta => ta.ReferenceId)
.Distinct()
.ToListAsync();
// Step 4: Try to identify the source of the attachment (task or comment)
var task = await _context.TaskAllocations
.Include(t => t.ReportedBy)
.FirstOrDefaultAsync(t => referenceIds.Contains(t.Id));
TaskComment? comment = null;
WorkItem? workItem = null;
Employee? uploadedBy = null;
string source = "";
if (task != null)
{
uploadedBy = task.ReportedBy;
workItem = await _context.WorkItems
.Include(wi => wi.ActivityMaster)
.Include(wi => wi.WorkCategoryMaster)
.FirstOrDefaultAsync(wi => wi.Id == task.WorkItemId);
source = "Report";
}
else
{
comment = await _context.TaskComments
.Include(tc => tc.TaskAllocation)
.Include(tc => tc.Employee)
.FirstOrDefaultAsync(tc => referenceIds.Contains(tc.Id));
var workItemId = comment?.TaskAllocation?.WorkItemId;
uploadedBy = comment?.Employee;
workItem = await _context.WorkItems
.Include(wi => wi.ActivityMaster)
.Include(wi => wi.WorkCategoryMaster)
.FirstOrDefaultAsync(wi => wi.Id == workItemId);
source = "Comment";
}
// Step 5: Traverse up to building level
var workAreaId = workItem?.WorkAreaId;
var workArea = await _context.WorkAreas
.Include(wa => wa.Floor)
.FirstOrDefaultAsync(wa => wa.Id == workAreaId);
var buildingId = workArea?.Floor?.BuildingId;
var building = await _context.Buildings
.FirstOrDefaultAsync(b => b.Id == buildingId);
// Step 6: Construct the response
var response = new
{
BatchId = batchId,
Documents = documents?.Select(x => new
{
Id = x.Id,
thumbnailUrl = x.ThumbS3Key != null ? _s3Service.GeneratePreSignedUrlAsync(x.ThumbS3Key) : (x.S3Key != null ? _s3Service.GeneratePreSignedUrlAsync(x.S3Key) : null),
Url = x.S3Key != null ? _s3Service.GeneratePreSignedUrlAsync(x.S3Key) : null,
UploadedBy = x.UploadedBy?.ToBasicEmployeeVMFromEmployee() ?? uploadedBy?.ToBasicEmployeeVMFromEmployee(),
UploadedAt = x.UploadedAt,
}).ToList(),
Source = source,
ProjectId = building?.ProjectId,
BuildingId = building?.Id,
BuildingName = building?.Name,
FloorIds = workArea?.Floor?.Id,
FloorName = workArea?.Floor?.FloorName,
WorkAreaId = workArea?.Id,
WorkAreaName = workArea?.AreaName,
TaskId = task?.Id,
ActivityId = workItem?.ActivityMaster?.Id,
ActivityName = workItem?.ActivityMaster?.ActivityName,
WorkCategoryId = workItem?.WorkCategoryMaster?.Id,
WorkCategoryName = workItem?.WorkCategoryMaster?.Name,
CommentId = comment?.Id,
Comment = comment?.Comment
};
_logger.LogInfo("Fetched {Count} image(s) for BatchId: {BatchId}", response.Documents?.Count ?? 0, batchId);
return Ok(ApiResponse<object>.SuccessResponse(response, "Images for provided batchId fetched successfully", 200));
}
[HttpGet("{documentId}")]
public async Task<IActionResult> GetImage(Guid documentId)
{
// Log the start of the image fetch process
_logger.LogInfo("GetImage called for DocumentId: {DocumentId}", documentId);
// Step 1: Get the currently logged-in employee (for future use like permission checks or auditing)
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// Step 2: Fetch the document from the database based on the provided ID
var document = await _context.Documents.FirstOrDefaultAsync(d => d.Id == documentId);
// Step 3: If document doesn't exist, return a 400 Bad Request response
if (document == null)
{
_logger.LogWarning("Document not found for DocumentId: {DocumentId}", documentId);
return BadRequest(ApiResponse<object>.ErrorResponse("Document not found", "Document not found", 400));
}
// Step 4: Generate pre-signed URLs for thumbnail and full image (if keys exist)
string? thumbnailUrl = document.ThumbS3Key != null
? _s3Service.GeneratePreSignedUrlAsync(document.ThumbS3Key)
: null;
string? imageUrl = document.S3Key != null
? _s3Service.GeneratePreSignedUrlAsync(document.S3Key)
: null;
// Step 5: Prepare the response object
var response = new
{
ThumbnailUrl = thumbnailUrl,
ImageUrl = imageUrl
};
// Step 6: Log successful fetch and return the result
_logger.LogInfo("Image fetched successfully for DocumentId: {DocumentId}", documentId);
return Ok(ApiResponse<object>.SuccessResponse(response, "Image fetched successfully", 200));
}
}
}

View File

@ -7,11 +7,13 @@ using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Activities; using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Helpers;
using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service; using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Document = Marco.Pms.Model.DocumentManager.Document; using Document = Marco.Pms.Model.DocumentManager.Document;
@ -28,17 +30,20 @@ namespace MarcoBMS.Services.Controllers
private readonly UserHelper _userHelper; private readonly UserHelper _userHelper;
private readonly S3UploadService _s3Service; private readonly S3UploadService _s3Service;
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
private readonly IHubContext<MarcoHub> _signalR;
private readonly PermissionServices _permissionServices; private readonly PermissionServices _permissionServices;
private readonly CacheUpdateHelper _cache; private readonly CacheUpdateHelper _cache;
private readonly Guid Approve_Task; private readonly Guid Approve_Task;
private readonly Guid Assign_Report_Task; private readonly Guid Assign_Report_Task;
public TaskController(ApplicationDbContext context, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permissionServices, CacheUpdateHelper cache) public TaskController(ApplicationDbContext context, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permissionServices,
IHubContext<MarcoHub> signalR, CacheUpdateHelper cache)
{ {
_context = context; _context = context;
_userHelper = userHelper; _userHelper = userHelper;
_s3Service = s3Service; _s3Service = s3Service;
_logger = logger; _logger = logger;
_signalR = signalR;
_permissionServices = permissionServices; _permissionServices = permissionServices;
_cache = cache; _cache = cache;
Approve_Task = Guid.Parse("db4e40c5-2ba9-4b6d-b8a6-a16a250ff99c"); Approve_Task = Guid.Parse("db4e40c5-2ba9-4b6d-b8a6-a16a250ff99c");
@ -199,8 +204,8 @@ namespace MarcoBMS.Services.Controllers
var comment = reportTask.ToCommentFromReportTaskDto(tenantId, loggedInEmployee.Id); var comment = reportTask.ToCommentFromReportTaskDto(tenantId, loggedInEmployee.Id);
_context.TaskComments.Add(comment); _context.TaskComments.Add(comment);
if (reportTask.Images?.Any() == true) int numberofImages = 0;
{
var workAreaId = taskAllocation.WorkItem?.WorkAreaId; var workAreaId = taskAllocation.WorkItem?.WorkAreaId;
var workArea = await _context.WorkAreas.Include(a => a.Floor) var workArea = await _context.WorkAreas.Include(a => a.Floor)
.FirstOrDefaultAsync(a => a.Id == workAreaId) ?? new WorkArea(); .FirstOrDefaultAsync(a => a.Id == workAreaId) ?? new WorkArea();
@ -209,6 +214,12 @@ namespace MarcoBMS.Services.Controllers
var building = await _context.Buildings var building = await _context.Buildings
.FirstOrDefaultAsync(b => b.Id == buildingId); .FirstOrDefaultAsync(b => b.Id == buildingId);
var batchId = Guid.NewGuid();
var projectId = building?.ProjectId;
if (reportTask.Images?.Any() == true)
{
foreach (var image in reportTask.Images) foreach (var image in reportTask.Images)
{ {
@ -224,16 +235,18 @@ namespace MarcoBMS.Services.Controllers
var fileType = _s3Service.GetContentTypeFromBase64(base64); var fileType = _s3Service.GetContentTypeFromBase64(base64);
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_report"); var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_report");
var objectKey = $"tenant-{tenantId}/project-{building?.ProjectId}/Actitvity/{fileName}"; var objectKey = $"tenant-{tenantId}/project-{projectId}/Actitvity/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey); await _s3Service.UploadFileAsync(base64, fileType, objectKey);
var document = new Document var document = new Document
{ {
BatchId = batchId,
UploadedById = loggedInEmployee.Id,
FileName = image.FileName ?? "", FileName = image.FileName ?? "",
ContentType = image.ContentType ?? "", ContentType = image.ContentType ?? "",
S3Key = objectKey, S3Key = objectKey,
Base64Data = image.Base64Data, //Base64Data = image.Base64Data,
FileSize = image.FileSize, FileSize = image.FileSize,
UploadedAt = DateTime.UtcNow, UploadedAt = DateTime.UtcNow,
TenantId = tenantId TenantId = tenantId
@ -246,6 +259,7 @@ namespace MarcoBMS.Services.Controllers
ReferenceId = reportTask.Id ReferenceId = reportTask.Id
}; };
_context.TaskAttachments.Add(attachment); _context.TaskAttachments.Add(attachment);
numberofImages += 1;
} }
} }
@ -263,6 +277,9 @@ namespace MarcoBMS.Services.Controllers
response.Comments = comments.Select(c => c.ToCommentVMFromTaskComment()).ToList(); response.Comments = comments.Select(c => c.ToCommentVMFromTaskComment()).ToList();
response.checkList = checkListVMs; response.checkList = checkListVMs;
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Task_Report", NumberOfImages = numberofImages, ProjectId = projectId };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
_logger.LogInfo("Task {TaskId} reported successfully by Employee {EmployeeId}", taskAllocation.Id, loggedInEmployee.Id); _logger.LogInfo("Task {TaskId} reported successfully by Employee {EmployeeId}", taskAllocation.Id, loggedInEmployee.Id);
return Ok(ApiResponse<object>.SuccessResponse(response, "Task reported successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(response, "Task reported successfully", 200));
@ -274,7 +291,7 @@ namespace MarcoBMS.Services.Controllers
_logger.LogInfo("AddCommentForTask called for TaskAllocationId: {TaskId}", createComment.TaskAllocationId); _logger.LogInfo("AddCommentForTask called for TaskAllocationId: {TaskId}", createComment.TaskAllocationId);
var tenantId = GetTenantId(); var tenantId = GetTenantId();
var employee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// Validate Task Allocation and associated WorkItem // Validate Task Allocation and associated WorkItem
var taskAllocation = await _context.TaskAllocations var taskAllocation = await _context.TaskAllocations
@ -294,15 +311,18 @@ namespace MarcoBMS.Services.Controllers
var buildingId = workArea.Floor?.BuildingId ?? Guid.Empty; var buildingId = workArea.Floor?.BuildingId ?? Guid.Empty;
var building = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == buildingId); var building = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == buildingId);
var projectId = building?.ProjectId;
// Save comment // Save comment
var comment = createComment.ToCommentFromCommentDto(tenantId, employee.Id); var comment = createComment.ToCommentFromCommentDto(tenantId, loggedInEmployee.Id);
_context.TaskComments.Add(comment); _context.TaskComments.Add(comment);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
_logger.LogInfo("Comment saved with Id: {CommentId}", comment.Id); _logger.LogInfo("Comment saved with Id: {CommentId}", comment.Id);
// Process image uploads // Process image uploads
var images = createComment.Images; var images = createComment.Images;
var batchId = Guid.NewGuid();
int numberofImages = 0;
if (images != null && images.Any()) if (images != null && images.Any())
{ {
@ -321,17 +341,19 @@ namespace MarcoBMS.Services.Controllers
var fileType = _s3Service.GetContentTypeFromBase64(base64); var fileType = _s3Service.GetContentTypeFromBase64(base64);
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_comment"); var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_comment");
var objectKey = $"tenant-{tenantId}/project-{building?.ProjectId}/Activity/{fileName}"; var objectKey = $"tenant-{tenantId}/project-{projectId}/Activity/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey); await _s3Service.UploadFileAsync(base64, fileType, objectKey);
_logger.LogInfo("Image uploaded to S3 with key: {ObjectKey}", objectKey); _logger.LogInfo("Image uploaded to S3 with key: {ObjectKey}", objectKey);
var document = new Document var document = new Document
{ {
BatchId = batchId,
UploadedById = loggedInEmployee.Id,
FileName = image.FileName ?? string.Empty, FileName = image.FileName ?? string.Empty,
ContentType = image.ContentType ?? fileType, ContentType = image.ContentType ?? fileType,
S3Key = objectKey, S3Key = objectKey,
Base64Data = image.Base64Data, //Base64Data = image.Base64Data,
FileSize = image.FileSize, FileSize = image.FileSize,
UploadedAt = DateTime.UtcNow, UploadedAt = DateTime.UtcNow,
TenantId = tenantId TenantId = tenantId
@ -346,6 +368,7 @@ namespace MarcoBMS.Services.Controllers
}; };
_context.TaskAttachments.Add(attachment); _context.TaskAttachments.Add(attachment);
numberofImages += 1;
} }
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@ -356,6 +379,9 @@ namespace MarcoBMS.Services.Controllers
var response = comment.ToCommentVMFromTaskComment(); var response = comment.ToCommentVMFromTaskComment();
_logger.LogInfo("Returning response for commentId: {CommentId}", comment.Id); _logger.LogInfo("Returning response for commentId: {CommentId}", comment.Id);
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Task_Comment", NumberOfImages = numberofImages, ProjectId = projectId };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
return Ok(ApiResponse<object>.SuccessResponse(response, "Comment saved successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(response, "Comment saved successfully", 200));
} }
@ -729,9 +755,6 @@ namespace MarcoBMS.Services.Controllers
}; };
_context.TaskComments.Add(comment); _context.TaskComments.Add(comment);
// Handle image attachments, if any
if (approveTask.Images?.Count > 0)
{
var workAreaId = taskAllocation.WorkItem?.WorkAreaId; var workAreaId = taskAllocation.WorkItem?.WorkAreaId;
var workArea = await _context.WorkAreas.Include(a => a.Floor) var workArea = await _context.WorkAreas.Include(a => a.Floor)
.FirstOrDefaultAsync(a => a.Id == workAreaId) ?? new WorkArea(); .FirstOrDefaultAsync(a => a.Id == workAreaId) ?? new WorkArea();
@ -740,6 +763,13 @@ namespace MarcoBMS.Services.Controllers
var building = await _context.Buildings var building = await _context.Buildings
.FirstOrDefaultAsync(b => b.Id == buildingId); .FirstOrDefaultAsync(b => b.Id == buildingId);
var projectId = building?.ProjectId;
int numberofImages = 0;
// Handle image attachments, if any
if (approveTask.Images?.Count > 0)
{
var batchId = Guid.NewGuid();
foreach (var image in approveTask.Images) foreach (var image in approveTask.Images)
{ {
@ -753,16 +783,18 @@ namespace MarcoBMS.Services.Controllers
var fileType = _s3Service.GetContentTypeFromBase64(base64); var fileType = _s3Service.GetContentTypeFromBase64(base64);
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_comment"); var fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_comment");
var objectKey = $"tenant-{tenantId}/project-{building?.ProjectId}/Activity/{fileName}"; var objectKey = $"tenant-{tenantId}/project-{projectId}/Activity/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey); await _s3Service.UploadFileAsync(base64, fileType, objectKey);
var document = new Document var document = new Document
{ {
BatchId = batchId,
UploadedById = loggedInEmployee.Id,
FileName = fileName, FileName = fileName,
ContentType = image.ContentType ?? string.Empty, ContentType = image.ContentType ?? string.Empty,
S3Key = objectKey, S3Key = objectKey,
Base64Data = image.Base64Data, //Base64Data = image.Base64Data,
FileSize = image.FileSize, FileSize = image.FileSize,
UploadedAt = DateTime.UtcNow, UploadedAt = DateTime.UtcNow,
TenantId = tenantId TenantId = tenantId
@ -779,12 +811,16 @@ namespace MarcoBMS.Services.Controllers
_context.TaskAttachments.Add(attachment); _context.TaskAttachments.Add(attachment);
_logger.LogInfo("Attachment uploaded for Task {TaskId}: {FileName}", approveTask.Id, fileName); _logger.LogInfo("Attachment uploaded for Task {TaskId}: {FileName}", approveTask.Id, fileName);
numberofImages += 1;
} }
} }
// Commit all changes to the database // Commit all changes to the database
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Task_Report", NumberOfImages = numberofImages, ProjectId = projectId };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
_logger.LogInfo("Task {TaskId} successfully approved by Employee {EmployeeId}", approveTask.Id, loggedInEmployee.Id); _logger.LogInfo("Task {TaskId} successfully approved by Employee {EmployeeId}", approveTask.Id, loggedInEmployee.Id);
return Ok(ApiResponse<object>.SuccessResponse("Task has been approved", "Task has been approved", 200)); return Ok(ApiResponse<object>.SuccessResponse("Task has been approved", "Task has been approved", 200));