Compare commits

...

18 Commits

Author SHA1 Message Date
39378f3a88 Resolved rebase issues 2025-06-16 18:09:00 +05:30
caacb43aa8 Merge branch 'SingalR_Integration' of https://git.marcoaiot.com/admin/marco.pms.api into SingalR_Integration 2025-06-16 17:57:49 +05:30
29ea1698bc Implemented signalR in manage Task API 2025-06-16 17:57:44 +05:30
793877b8f8 Implemented signalR in project infrastructure APIs 2025-06-16 17:57:37 +05:30
f47586710b Add basic implementation and add in record attendance API for testing 2025-06-16 17:55:59 +05:30
b21d30c18e Changed the keyword for updating and creating project 2025-06-16 17:54:52 +05:30
d78a2fe3b2 Implemented signalR in project infrastructure APIs 2025-06-16 17:54:52 +05:30
6ebc74499f Add basic implementation and add in record attendance API for testing 2025-06-16 17:54:52 +05:30
9d5535edf1 Added description in list task API 2025-06-12 22:30:43 +05:30
c689f2dfd8 Merge branch 'Acititvity_Images' of https://git.marcoaiot.com/admin/marco.pms.api into Acititvity_Images 2025-06-12 21:40:36 +05:30
5c019a2ff6 Chnaged the name form presignedUrls to ReportedProsignedUrl 2025-06-12 21:40:31 +05:30
cb185db4f3 Sending pre signed url in task list API 2025-06-12 21:40:31 +05:30
1a51860517 Implemented the image capture while writing comment in task allocation and add presignedurl in task get by in API 2025-06-12 21:40:31 +05:30
f275d08215 Implemented image storing to S3 while reporting task 2025-06-12 21:40:31 +05:30
4ccc690560 Chnaged the name form presignedUrls to ReportedProsignedUrl 2025-06-12 21:35:40 +05:30
691a670a28 Sending pre signed url in task list API 2025-06-12 18:32:50 +05:30
76b6ac6581 Implemented the image capture while writing comment in task allocation and add presignedurl in task get by in API 2025-06-12 17:02:18 +05:30
abe7870ad5 Implemented image storing to S3 while reporting task 2025-06-12 16:09:28 +05:30
18 changed files with 3627 additions and 74 deletions

View File

@ -44,6 +44,7 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<TaskAllocation> TaskAllocations { get; set; } public DbSet<TaskAllocation> TaskAllocations { get; set; }
public DbSet<TaskComment> TaskComments { get; set; } public DbSet<TaskComment> TaskComments { get; set; }
public DbSet<TaskMembers> TaskMembers { get; set; } public DbSet<TaskMembers> TaskMembers { get; set; }
public DbSet<TaskAttachment> TaskAttachments { get; set; }
public DbSet<Attendance> Attendes { get; set; } public DbSet<Attendance> Attendes { get; set; }
public DbSet<AttendanceLog> AttendanceLogs { get; set; } public DbSet<AttendanceLog> AttendanceLogs { get; set; }
public DbSet<Employee> Employees { get; set; } public DbSet<Employee> Employees { get; set; }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_TaskAttachments_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "TaskAttachments",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ReferenceId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
DocumentId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_TaskAttachments", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TaskAttachments");
}
}
}

View File

@ -63,6 +63,23 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("TaskAllocations"); b.ToTable("TaskAllocations");
}); });
modelBuilder.Entity("Marco.Pms.Model.Activities.TaskAttachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("DocumentId")
.HasColumnType("char(36)");
b.Property<Guid>("ReferenceId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.ToTable("TaskAttachments");
});
modelBuilder.Entity("Marco.Pms.Model.Activities.TaskComment", b => modelBuilder.Entity("Marco.Pms.Model.Activities.TaskComment", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")

View File

@ -0,0 +1,9 @@
namespace Marco.Pms.Model.Activities
{
public class TaskAttachment
{
public Guid Id { get; set; }
public Guid ReferenceId { get; set; }
public Guid DocumentId { get; set; }
}
}

View File

@ -1,17 +0,0 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
namespace Marco.Pms.Model.Activities
{
public class TaskImages
{
public Guid Id { get; set; }
public Guid TaskAllocationId { get; set; }
[ValidateNever]
[ForeignKey(nameof(TaskAllocationId))]
public TaskAllocation? TaskAllocation { get; set; }
public string? ImagePath { get; set; }
}
}

View File

@ -1,11 +1,12 @@
namespace Marco.Pms.Model.Dtos.Activities using Marco.Pms.Model.Utilities;
namespace Marco.Pms.Model.Dtos.Activities
{ {
public class CreateCommentDto public class CreateCommentDto
{ {
public Guid TaskAllocationId { get; set; } public Guid TaskAllocationId { get; set; }
public DateTime CommentDate { get; set; } public DateTime CommentDate { get; set; }
public string? Comment { get; set; } public string? Comment { get; set; }
public List<FileUploadModel>? Images { get; set; }
} }
} }

View File

@ -1,4 +1,6 @@
namespace Marco.Pms.Model.Dtos.Activities using Marco.Pms.Model.Utilities;
namespace Marco.Pms.Model.Dtos.Activities
{ {
public class ReportTaskDto public class ReportTaskDto
{ {
@ -7,5 +9,6 @@
public DateTime ReportedDate { get; set; } public DateTime ReportedDate { get; set; }
public string? Comment { get; set; } public string? Comment { get; set; }
public List<ReportCheckListDto>? CheckList { get; set; } public List<ReportCheckListDto>? CheckList { get; set; }
public List<FileUploadModel>? Images { get; set; }
} }
} }

View File

@ -8,7 +8,7 @@
public List<UpdateContactPhoneDto>? ContactPhones { get; set; } public List<UpdateContactPhoneDto>? ContactPhones { get; set; }
public List<UpdateContactEmailDto>? ContactEmails { get; set; } public List<UpdateContactEmailDto>? ContactEmails { get; set; }
public List<Guid>? BucketIds { get; set; } public List<Guid>? BucketIds { get; set; }
public Guid ContactCategoryId { get; set; } public Guid? ContactCategoryId { get; set; }
public string? Description { get; set; } public string? Description { get; set; }
public string? Organization { get; set; } public string? Organization { get; set; }
public string? Address { get; set; } public string? Address { get; set; }

View File

@ -101,6 +101,7 @@ namespace Marco.Pms.Model.Mapper
{ {
Id = taskAllocation.Id, Id = taskAllocation.Id,
AssignmentDate = taskAllocation.AssignmentDate, AssignmentDate = taskAllocation.AssignmentDate,
Description = taskAllocation.Description,
PlannedTask = taskAllocation.PlannedTask, PlannedTask = taskAllocation.PlannedTask,
ReportedDate = taskAllocation.ReportedDate, ReportedDate = taskAllocation.ReportedDate,
CompletedTask = taskAllocation.CompletedTask, CompletedTask = taskAllocation.CompletedTask,

View File

@ -8,5 +8,6 @@
public string? Comment { get; set; } public string? Comment { get; set; }
public Guid CommentedBy { get; set; } public Guid CommentedBy { get; set; }
public BasicEmployeeVM? Employee { get; set; } public BasicEmployeeVM? Employee { get; set; }
public List<string>? PreSignedUrls { get; set; }
} }
} }

View File

@ -10,7 +10,9 @@ namespace Marco.Pms.Model.ViewModels.Activities
public double PlannedTask { get; set; } public double PlannedTask { get; set; }
public double CompletedTask { get; set; } public double CompletedTask { get; set; }
public BasicEmployeeVM? AssignedBy { get; set; } public BasicEmployeeVM? AssignedBy { get; set; }
public string? Description { get; set; }
public Guid WorkItemId { get; set; } public Guid WorkItemId { get; set; }
public List<string>? ReportedPreSignedUrls { get; set; }
public WorkItem? WorkItem { get; set; } public WorkItem? WorkItem { get; set; }
public List<BasicEmployeeVM>? teamMembers { get; set; } public List<BasicEmployeeVM>? teamMembers { get; set; }
public List<CommentVM>? comments { get; set; } public List<CommentVM>? comments { get; set; }

View File

@ -10,7 +10,6 @@
public string? Description { get; set; } public string? Description { get; set; }
public Guid AssignedBy { get; set; } public Guid AssignedBy { get; set; }
public Guid WorkItemId { get; set; } public Guid WorkItemId { get; set; }
public List<CommentVM>? Comments { get; set; } public List<CommentVM>? Comments { get; set; }
public List<CheckListVM>? checkList { get; set; } public List<CheckListVM>? checkList { get; set; }
} }

View File

@ -13,6 +13,7 @@ namespace Marco.Pms.Model.ViewModels.Activities
public string? Description { get; set; } public string? Description { get; set; }
public string? AssignBy { get; set; } public string? AssignBy { get; set; }
public WorkItem? WorkItem { get; set; } public WorkItem? WorkItem { get; set; }
public List<string>? PreSignedUrls { get; set; }
public List<CommentVM>? Comments { get; set; } public List<CommentVM>? Comments { get; set; }
public List<EmployeeVM>? TeamMembers { get; set; } public List<EmployeeVM>? TeamMembers { get; set; }
} }

View File

@ -699,7 +699,6 @@ namespace MarcoBMS.Services.Controllers
} }
Document? document = null; Document? document = null;
var Image = recordAttendanceDot.Image; var Image = recordAttendanceDot.Image;
var objectKey = string.Empty;
var preSignedUrl = string.Empty; var preSignedUrl = string.Empty;
if (Image != null && Image.ContentType != null) if (Image != null && Image.ContentType != null)
@ -708,7 +707,16 @@ namespace MarcoBMS.Services.Controllers
if (string.IsNullOrEmpty(Image.Base64Data)) if (string.IsNullOrEmpty(Image.Base64Data))
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, TenantId, "attendance"); //If base64 has a data URI prefix, strip it
var base64 = Image.Base64Data.Contains(",")
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
: Image.Base64Data;
string fileType = _s3Service.GetContentTypeFromBase64(base64);
string fileName = _s3Service.GenerateFileName(fileType, TenantId, "attendance");
string objectKey = $"tenant-{TenantId}/Employee/{recordAttendanceDot.EmployeeID}/Attendance/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(objectKey); preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(objectKey);
document = new Document document = new Document
@ -723,6 +731,7 @@ namespace MarcoBMS.Services.Controllers
}; };
_context.Documents.Add(document); _context.Documents.Add(document);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
} }

View File

@ -68,7 +68,16 @@ namespace Marco.Pms.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
} }
var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "forum"); //If base64 has a data URI prefix, strip it
var base64 = Image.Base64Data.Contains(",")
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
: Image.Base64Data;
string fileType = _s3Service.GetContentTypeFromBase64(base64);
string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");
string objectKey = $"tenant-{tenantId}/project-{createTicketDto.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, createTicketDto.CreatedAt, tenantId); Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, createTicketDto.CreatedAt, tenantId);
_context.Documents.Add(document); _context.Documents.Add(document);
@ -182,7 +191,16 @@ namespace Marco.Pms.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
} }
var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "forum"); //If base64 has a data URI prefix, strip it
var base64 = Image.Base64Data.Contains(",")
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
: Image.Base64Data;
string fileType = _s3Service.GetContentTypeFromBase64(base64);
string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");
string objectKey = $"tenant-{tenantId}/project-{updateTicketDto.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, updateTicketDto.CreatedAt, tenantId); Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, updateTicketDto.CreatedAt, tenantId);
_context.Documents.Add(document); _context.Documents.Add(document);
@ -329,6 +347,14 @@ namespace Marco.Pms.Services.Controllers
List<TicketAttachment> attachments = new List<TicketAttachment>(); List<TicketAttachment> attachments = new List<TicketAttachment>();
List<Document> documents = new List<Document>(); List<Document> documents = new List<Document>();
TicketForum? ticket = await _context.Tickets.FirstOrDefaultAsync(t => t.Id == addCommentDto.TicketId);
if (ticket == null)
{
_logger.LogError("Ticket {TicketId} not Found in database", addCommentDto.TicketId);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
}
TicketComment comment = addCommentDto.ToTicketCommentFromAddCommentDto(tenantId); TicketComment comment = addCommentDto.ToTicketCommentFromAddCommentDto(tenantId);
_context.TicketComments.Add(comment); _context.TicketComments.Add(comment);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@ -344,7 +370,16 @@ namespace Marco.Pms.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
} }
var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "forum"); //If base64 has a data URI prefix, strip it
var base64 = Image.Base64Data.Contains(",")
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
: Image.Base64Data;
string fileType = _s3Service.GetContentTypeFromBase64(base64);
string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");
string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, addCommentDto.SentAt, tenantId); Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, addCommentDto.SentAt, tenantId);
_context.Documents.Add(document); _context.Documents.Add(document);
@ -396,6 +431,14 @@ namespace Marco.Pms.Services.Controllers
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
List<TicketAttachment> attachments = new List<TicketAttachment>(); List<TicketAttachment> attachments = new List<TicketAttachment>();
TicketForum? ticket = await _context.Tickets.FirstOrDefaultAsync(t => t.Id == updateCommentDto.TicketId);
if (ticket == null)
{
_logger.LogError("Ticket {TicketId} not Found in database", updateCommentDto.TicketId);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
}
TicketComment existingComment = await _context.TicketComments.AsNoTracking().FirstOrDefaultAsync(c => c.Id == updateCommentDto.Id) ?? new TicketComment(); TicketComment existingComment = await _context.TicketComments.AsNoTracking().FirstOrDefaultAsync(c => c.Id == updateCommentDto.Id) ?? new TicketComment();
TicketComment updateComment = updateCommentDto.ToTicketCommentFromUpdateCommentDto(tenantId, existingComment); TicketComment updateComment = updateCommentDto.ToTicketCommentFromUpdateCommentDto(tenantId, existingComment);
_context.TicketComments.Update(updateComment); _context.TicketComments.Update(updateComment);
@ -419,7 +462,16 @@ namespace Marco.Pms.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
} }
var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "forum"); //If base64 has a data URI prefix, strip it
var base64 = Image.Base64Data.Contains(",")
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
: Image.Base64Data;
string fileType = _s3Service.GetContentTypeFromBase64(base64);
string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");
string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, existingComment.SentAt, tenantId); Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, existingComment.SentAt, tenantId);
_context.Documents.Add(document); _context.Documents.Add(document);
@ -491,6 +543,16 @@ namespace Marco.Pms.Services.Controllers
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
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<TicketForum> tickets = await _context.Tickets.Where(t => ticketIds.Contains(t.Id)).ToListAsync();
if (tickets == null || tickets.Count > 0)
{
_logger.LogError("Tickets not Found in database");
return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
}
TicketAttachment attachment = new TicketAttachment(); TicketAttachment attachment = new TicketAttachment();
foreach (var forumAttachmentDto in forumAttachmentDtos) foreach (var forumAttachmentDto in forumAttachmentDtos)
@ -505,7 +567,17 @@ namespace Marco.Pms.Services.Controllers
_logger.LogError("ticket ID is missing"); _logger.LogError("ticket ID is missing");
return BadRequest(ApiResponse<object>.ErrorResponse("ticket ID is missing", "ticket ID is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("ticket ID is missing", "ticket ID is missing", 400));
} }
var objectKey = await _s3Service.UploadFileAsync(forumAttachmentDto.Base64Data, tenantId, "forum"); var ticket = tickets.FirstOrDefault(t => t.Id == forumAttachmentDto.TicketId);
//If base64 has a data URI prefix, strip it
var base64 = forumAttachmentDto.Base64Data.Contains(",")
? forumAttachmentDto.Base64Data.Substring(forumAttachmentDto.Base64Data.IndexOf(",") + 1)
: forumAttachmentDto.Base64Data;
string fileType = _s3Service.GetContentTypeFromBase64(base64);
string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");
string objectKey = $"tenant-{tenantId}/project-{ticket?.LinkedProjectId}/froum/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = forumAttachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, forumAttachmentDto.SentAt, tenantId); Document document = forumAttachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, forumAttachmentDto.SentAt, tenantId);
_context.Documents.Add(document); _context.Documents.Add(document);

View File

@ -8,10 +8,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.Model.ViewModels.Employee; using Marco.Pms.Model.ViewModels.Employee;
using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Document = Marco.Pms.Model.DocumentManager.Document;
namespace MarcoBMS.Services.Controllers namespace MarcoBMS.Services.Controllers
{ {
@ -23,12 +26,14 @@ namespace MarcoBMS.Services.Controllers
{ {
private readonly ApplicationDbContext _context; private readonly ApplicationDbContext _context;
private readonly UserHelper _userHelper; private readonly UserHelper _userHelper;
private readonly S3UploadService _s3Service;
public TaskController(ApplicationDbContext context, UserHelper userHelper) public TaskController(ApplicationDbContext context, UserHelper userHelper, S3UploadService s3Service)
{ {
_context = context; _context = context;
_userHelper = userHelper; _userHelper = userHelper;
_s3Service = s3Service;
} }
private Guid GetTenantId() private Guid GetTenantId()
@ -84,7 +89,6 @@ namespace MarcoBMS.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(response, "Task assignned successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(response, "Task assignned successfully", 200));
} }
[HttpPost("report")] [HttpPost("report")]
public async Task<IActionResult> ReportTaskProgress([FromBody] ReportTaskDto reportTask) public async Task<IActionResult> ReportTaskProgress([FromBody] ReportTaskDto reportTask)
{ {
@ -104,10 +108,13 @@ namespace MarcoBMS.Services.Controllers
var checkListIds = reportTask.CheckList != null ? reportTask.CheckList.Select(c => c.Id).ToList() : new List<Guid>(); var checkListIds = reportTask.CheckList != null ? reportTask.CheckList.Select(c => c.Id).ToList() : new List<Guid>();
var checkList = await _context.ActivityCheckLists.Where(c => checkListIds.Contains(c.Id)).ToListAsync(); var checkList = await _context.ActivityCheckLists.Where(c => checkListIds.Contains(c.Id)).ToListAsync();
if (taskAllocation == null) if (taskAllocation == null || taskAllocation.WorkItem == null)
{ {
return BadRequest(ApiResponse<object>.ErrorResponse("No such task has been allocated.", "No such task has been allocated.", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("No such task has been allocated.", "No such task has been allocated.", 400));
} }
WorkArea workArea = await _context.WorkAreas.Include(a => a.Floor).FirstOrDefaultAsync(a => a.Id == taskAllocation.WorkItem.WorkAreaId) ?? new WorkArea();
var bulding = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == (workArea.Floor != null ? workArea.Floor.BuildingId : Guid.Empty));
if (taskAllocation.WorkItem != null) if (taskAllocation.WorkItem != null)
{ {
if (taskAllocation.CompletedTask != 0) if (taskAllocation.CompletedTask != 0)
@ -143,6 +150,49 @@ namespace MarcoBMS.Services.Controllers
_context.CheckListMappings.AddRange(checkListMappings); _context.CheckListMappings.AddRange(checkListMappings);
var comment = reportTask.ToCommentFromReportTaskDto(tenantId, Employee.Id); var comment = reportTask.ToCommentFromReportTaskDto(tenantId, Employee.Id);
var Images = reportTask.Images;
if (Images != null && Images.Count > 0)
{
foreach (var Image in Images)
{
if (string.IsNullOrEmpty(Image.Base64Data))
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
//If base64 has a data URI prefix, strip it
var base64 = Image.Base64Data.Contains(",")
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
: Image.Base64Data;
string fileType = _s3Service.GetContentTypeFromBase64(base64);
string fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_report");
string objectKey = $"tenant-{tenantId}/project-{bulding?.ProjectId}/Actitvity/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = new Document
{
FileName = Image.FileName ?? "",
ContentType = Image.ContentType ?? "",
S3Key = objectKey,
Base64Data = Image.Base64Data,
FileSize = Image.FileSize,
UploadedAt = DateTime.UtcNow,
TenantId = tenantId
};
_context.Documents.Add(document);
TaskAttachment attachment = new TaskAttachment
{
DocumentId = document.Id,
ReferenceId = reportTask.Id
};
_context.TaskAttachments.Add(attachment);
}
await _context.SaveChangesAsync();
}
_context.TaskComments.Add(comment); _context.TaskComments.Add(comment);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@ -164,10 +214,62 @@ namespace MarcoBMS.Services.Controllers
var tenantId = GetTenantId(); var tenantId = GetTenantId();
var Employee = await _userHelper.GetCurrentEmployeeAsync(); var Employee = await _userHelper.GetCurrentEmployeeAsync();
var taskAllocation = await _context.TaskAllocations.Include(t => t.WorkItem).FirstOrDefaultAsync(t => t.Id == createComment.TaskAllocationId);
if (taskAllocation == null || taskAllocation.WorkItem == null)
{
return BadRequest(ApiResponse<object>.ErrorResponse("No such task has been allocated.", "No such task has been allocated.", 400));
}
WorkArea workArea = await _context.WorkAreas.Include(a => a.Floor).FirstOrDefaultAsync(a => a.Id == taskAllocation.WorkItem.WorkAreaId) ?? new WorkArea();
var bulding = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == (workArea.Floor != null ? workArea.Floor.BuildingId : Guid.Empty));
var comment = createComment.ToCommentFromCommentDto(tenantId, Employee.Id); var comment = createComment.ToCommentFromCommentDto(tenantId, Employee.Id);
_context.TaskComments.Add(comment); _context.TaskComments.Add(comment);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
var Images = createComment.Images;
if (Images != null && Images.Count > 0)
{
foreach (var Image in Images)
{
if (string.IsNullOrEmpty(Image.Base64Data))
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
//If base64 has a data URI prefix, strip it
var base64 = Image.Base64Data.Contains(",")
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
: Image.Base64Data;
string fileType = _s3Service.GetContentTypeFromBase64(base64);
string fileName = _s3Service.GenerateFileName(fileType, tenantId, "task_report");
string objectKey = $"tenant-{tenantId}/project-{bulding?.ProjectId}/Actitvity/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
Document document = new Document
{
FileName = Image.FileName ?? "",
ContentType = Image.ContentType ?? "",
S3Key = objectKey,
Base64Data = Image.Base64Data,
FileSize = Image.FileSize,
UploadedAt = DateTime.UtcNow,
TenantId = tenantId
};
_context.Documents.Add(document);
TaskAttachment attachment = new TaskAttachment
{
DocumentId = document.Id,
ReferenceId = comment.Id
};
_context.TaskAttachments.Add(attachment);
}
await _context.SaveChangesAsync();
}
CommentVM response = comment.ToCommentVMFromTaskComment(); CommentVM response = comment.ToCommentVMFromTaskComment();
return Ok(ApiResponse<object>.SuccessResponse(response, "Comment saved successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(response, "Comment saved successfully", 200));
} }
@ -215,6 +317,13 @@ namespace MarcoBMS.Services.Controllers
List<Employee> employees = await _context.Employees.Where(e => employeeIdList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync(); List<Employee> employees = await _context.Employees.Where(e => employeeIdList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync();
List<TaskComment> allComments = await _context.TaskComments.Include(c => c.Employee).Where(c => taskIdList.Contains(c.TaskAllocationId)).ToListAsync();
var allCommentIds = allComments.Select(c => c.Id).ToList();
var taskAttachments = await _context.TaskAttachments.Where(t => taskIdList.Contains(t.ReferenceId) || allCommentIds.Contains(t.ReferenceId)).ToListAsync();
var documentIds = taskAttachments.Select(t => t.DocumentId).ToList();
var documents = await _context.Documents.Where(d => documentIds.Contains(d.Id)).ToListAsync();
List<ListTaskVM> tasks = new List<ListTaskVM>(); List<ListTaskVM> tasks = new List<ListTaskVM>();
//foreach (var workItem in workItems) //foreach (var workItem in workItems)
//{ //{
@ -223,10 +332,21 @@ namespace MarcoBMS.Services.Controllers
var response = taskAllocation.ToListTaskVMFromTaskAllocation(); var response = taskAllocation.ToListTaskVMFromTaskAllocation();
List<TaskComment> comments = await _context.TaskComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync(); List<TaskComment> comments = allComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToList();
List<BasicEmployeeVM> team = new List<BasicEmployeeVM>(); List<BasicEmployeeVM> team = new List<BasicEmployeeVM>();
List<TaskMembers> taskMembers = teamMembers.Where(m => m.TaskAllocationId == taskAllocation.Id).ToList(); List<TaskMembers> taskMembers = teamMembers.Where(m => m.TaskAllocationId == taskAllocation.Id).ToList();
var taskDocumentIds = taskAttachments.Where(t => t.ReferenceId == taskAllocation.Id).Select(t => t.DocumentId).ToList();
var taskDocuments = documents.Where(d => taskDocumentIds.Contains(d.Id)).ToList();
List<string> taskPreSignedUrls = new List<string>();
foreach (var document in taskDocuments)
{
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
taskPreSignedUrls.Add(preSignedUrl);
}
response.ReportedPreSignedUrls = taskPreSignedUrls;
foreach (var taskMember in taskMembers) foreach (var taskMember in taskMembers)
{ {
var teamMember = employees.Find(e => e.Id == taskMember.EmployeeId); var teamMember = employees.Find(e => e.Id == taskMember.EmployeeId);
@ -238,7 +358,18 @@ namespace MarcoBMS.Services.Controllers
List<CommentVM> commentVM = new List<CommentVM> { }; List<CommentVM> commentVM = new List<CommentVM> { };
foreach (var comment in comments) foreach (var comment in comments)
{ {
commentVM.Add(comment.ToCommentVMFromTaskComment()); var commentDocumentIds = taskAttachments.Where(t => t.ReferenceId == comment.Id).Select(t => t.DocumentId).ToList();
var commentDocuments = documents.Where(d => commentDocumentIds.Contains(d.Id)).ToList();
List<string> commentPreSignedUrls = new List<string>();
foreach (var document in commentDocuments)
{
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
commentPreSignedUrls.Add(preSignedUrl);
}
CommentVM commentVm = comment.ToCommentVMFromTaskComment();
commentVm.PreSignedUrls = commentPreSignedUrls;
commentVM.Add(commentVm);
} }
List<ActivityCheckList> checkLists = await _context.ActivityCheckLists.Where(x => x.ActivityId == (taskAllocation.WorkItem != null ? taskAllocation.WorkItem.ActivityId : Guid.Empty)).ToListAsync(); List<ActivityCheckList> checkLists = await _context.ActivityCheckLists.Where(x => x.ActivityId == (taskAllocation.WorkItem != null ? taskAllocation.WorkItem.ActivityId : Guid.Empty)).ToListAsync();
List<CheckListMappings> checkListMappings = await _context.CheckListMappings.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync(); List<CheckListMappings> checkListMappings = await _context.CheckListMappings.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync();
@ -280,8 +411,23 @@ namespace MarcoBMS.Services.Controllers
if (taskAllocation == null) return NotFound(ApiResponse<object>.ErrorResponse("Task Not Found", "Task not found", 404)); if (taskAllocation == null) return NotFound(ApiResponse<object>.ErrorResponse("Task Not Found", "Task not found", 404));
var taskVM = taskAllocation.TaskAllocationToTaskVM(employeeName); var taskVM = taskAllocation.TaskAllocationToTaskVM(employeeName);
var comments = await _context.TaskComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync(); var comments = await _context.TaskComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync();
var commentIds = comments.Select(c => c.Id).ToList();
var taskAttachments = await _context.TaskAttachments.Where(t => t.ReferenceId == taskAllocation.Id || commentIds.Contains(t.ReferenceId)).ToListAsync();
var documentIds = taskAttachments.Select(t => t.DocumentId).ToList();
var documents = await _context.Documents.Where(d => documentIds.Contains(d.Id)).ToListAsync();
var team = await _context.TaskMembers.Where(m => m.TaskAllocationId == taskAllocation.Id).Include(m => m.Employee).ToListAsync(); var team = await _context.TaskMembers.Where(m => m.TaskAllocationId == taskAllocation.Id).Include(m => m.Employee).ToListAsync();
var teamMembers = new List<EmployeeVM> { }; var teamMembers = new List<EmployeeVM> { };
var taskDocumentIds = taskAttachments.Where(t => t.ReferenceId == taskAllocation.Id).Select(t => t.DocumentId).ToList();
var taskDocuments = documents.Where(d => taskDocumentIds.Contains(d.Id)).ToList();
List<string> taskPreSignedUrls = new List<string>();
foreach (var document in taskDocuments)
{
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
taskPreSignedUrls.Add(preSignedUrl);
}
taskVM.PreSignedUrls = taskPreSignedUrls;
foreach (var member in team) foreach (var member in team)
{ {
var result = member.Employee != null ? member.Employee.ToEmployeeVMFromEmployee() : new EmployeeVM(); var result = member.Employee != null ? member.Employee.ToEmployeeVMFromEmployee() : new EmployeeVM();
@ -290,7 +436,17 @@ namespace MarcoBMS.Services.Controllers
List<CommentVM> Comments = new List<CommentVM> { }; List<CommentVM> Comments = new List<CommentVM> { };
foreach (var comment in comments) foreach (var comment in comments)
{ {
Comments.Add(comment.ToCommentVMFromTaskComment()); var commentDocumentIds = taskAttachments.Where(t => t.ReferenceId == comment.Id).Select(t => t.DocumentId).ToList();
var commentDocuments = documents.Where(d => commentDocumentIds.Contains(d.Id)).ToList();
List<string> commentPreSignedUrls = new List<string>();
foreach (var document in commentDocuments)
{
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
commentPreSignedUrls.Add(preSignedUrl);
}
CommentVM commentVM = comment.ToCommentVMFromTaskComment();
commentVM.PreSignedUrls = commentPreSignedUrls;
Comments.Add(commentVM);
} }
taskVM.Comments = Comments; taskVM.Comments = Comments;
taskVM.TeamMembers = teamMembers; taskVM.TeamMembers = teamMembers;

View File

@ -28,53 +28,46 @@ namespace Marco.Pms.Services.Service
_s3Client = new AmazonS3Client(settings.AccessKey, settings.SecretKey, region); _s3Client = new AmazonS3Client(settings.AccessKey, settings.SecretKey, region);
} }
//public async Task<string> UploadFileAsync(string fileName, string contentType) //public async Task<string> UploadFileAsync(string fileName, string contentType)
public async Task<string> UploadFileAsync(string base64Data, Guid tenantId, string tag) public async Task UploadFileAsync(string base64, string fileType, string objectKey)
{ {
byte[] fileBytes; byte[] fileBytes;
//If base64 has a data URI prefix, strip it
var base64 = base64Data.Contains(",")
? base64Data.Substring(base64Data.IndexOf(",") + 1)
: base64Data;
var allowedFilesType = _configuration.GetSection("WhiteList:ContentType") var allowedFilesType = _configuration.GetSection("WhiteList:ContentType")
.GetChildren() .GetChildren()
.Select(x => x.Value) .Select(x => x.Value)
.ToList(); .ToList();
string fileType = GetContentTypeFromBase64(base64);
if (allowedFilesType != null && allowedFilesType.Contains(fileType)) if (allowedFilesType == null || !allowedFilesType.Contains(fileType))
{ {
string fileName = GenerateFileName(fileType, tenantId, tag); throw new InvalidOperationException("Unsupported file type.");
fileBytes = Convert.FromBase64String(base64);
using var fileStream = new MemoryStream(fileBytes);
// Generate a unique object key (you can customize this)
var objectKey = $"{fileName}";
var uploadRequest = new TransferUtilityUploadRequest
{
InputStream = fileStream,
Key = objectKey,
BucketName = _bucketName,
ContentType = fileType,
AutoCloseStream = true
};
try
{
var transferUtility = new TransferUtility(_s3Client);
await transferUtility.UploadAsync(uploadRequest);
_logger.LogInfo("File uploaded to Amazon S3");
return objectKey;
}
catch (Exception ex)
{
_logger.LogError("{error} while uploading file to S3", ex.Message);
return string.Empty;
}
} }
throw new InvalidOperationException("Unsupported file type.");
fileBytes = Convert.FromBase64String(base64);
using var fileStream = new MemoryStream(fileBytes);
var uploadRequest = new TransferUtilityUploadRequest
{
InputStream = fileStream,
Key = objectKey,
BucketName = _bucketName,
ContentType = fileType,
AutoCloseStream = true
};
try
{
var transferUtility = new TransferUtility(_s3Client);
await transferUtility.UploadAsync(uploadRequest);
_logger.LogInfo("File uploaded to Amazon S3");
}
catch (Exception ex)
{
_logger.LogError("{error} while uploading file to S3", ex.Message);
}
} }
public string GeneratePreSignedUrlAsync(string objectKey) public string GeneratePreSignedUrlAsync(string objectKey)
{ {