Compare commits
	
		
			5 Commits
		
	
	
		
			65da812a97
			...
			85911c4536
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 85911c4536 | |||
| afdf51eae3 | |||
| 587e8d2b0b | |||
| b77a5b16cd | |||
| ba1e644fd8 | 
							
								
								
									
										3426
									
								
								Marco.Pms.DataAccess/Migrations/20250702042830_Added_UploadedBy_ForeginKey_In_Decuments_Table.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										3426
									
								
								Marco.Pms.DataAccess/Migrations/20250702042830_Added_UploadedBy_ForeginKey_In_Decuments_Table.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -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");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -752,10 +752,15 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                    b.Property<DateTime>("UploadedAt")
 | 
			
		||||
                        .HasColumnType("datetime(6)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<Guid?>("UploadedById")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.HasKey("Id");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("TenantId");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("UploadedById");
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("Documents");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
@ -2951,7 +2956,13 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                        .OnDelete(DeleteBehavior.Cascade)
 | 
			
		||||
                        .IsRequired();
 | 
			
		||||
 | 
			
		||||
                    b.HasOne("Marco.Pms.Model.Employees.Employee", "UploadedBy")
 | 
			
		||||
                        .WithMany()
 | 
			
		||||
                        .HasForeignKey("UploadedById");
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("Tenant");
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("UploadedBy");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("Marco.Pms.Model.Employees.Employee", b =>
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
{
 | 
			
		||||
@ -16,10 +19,15 @@ namespace Marco.Pms.Model.DocumentManager
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string? ThumbS3Key { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string? Base64Data { get; set; }
 | 
			
		||||
        public string? Base64Data { get; set; } = null;
 | 
			
		||||
 | 
			
		||||
        public long FileSize { get; set; }
 | 
			
		||||
        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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
            {
 | 
			
		||||
                BatchId = batchId,
 | 
			
		||||
                UploadedById = loggedInEmployeeId,
 | 
			
		||||
                FileName = AttachmentDto.FileName,
 | 
			
		||||
                ContentType = AttachmentDto.ContentType,
 | 
			
		||||
                S3Key = objectKey,
 | 
			
		||||
                ThumbS3Key = thumbS3Key,
 | 
			
		||||
                Base64Data = AttachmentDto.Base64Data,
 | 
			
		||||
                //Base64Data = AttachmentDto.Base64Data,
 | 
			
		||||
                FileSize = AttachmentDto.FileSize,
 | 
			
		||||
                UploadedAt = uploadedAt,
 | 
			
		||||
                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
 | 
			
		||||
            {
 | 
			
		||||
                BatchId = batchId,
 | 
			
		||||
                UploadedById = loggedInEmployeeId,
 | 
			
		||||
                FileName = AttachmentDto.FileName,
 | 
			
		||||
                ContentType = AttachmentDto.ContentType,
 | 
			
		||||
                S3Key = objectKey,
 | 
			
		||||
                ThumbS3Key = thumbS3Key,
 | 
			
		||||
                Base64Data = AttachmentDto.Base64Data,
 | 
			
		||||
                //Base64Data = AttachmentDto.Base64Data,
 | 
			
		||||
                FileSize = AttachmentDto.FileSize,
 | 
			
		||||
                UploadedAt = uploadedAt,
 | 
			
		||||
                TenantId = tenantId
 | 
			
		||||
 | 
			
		||||
@ -603,7 +603,8 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Guid tenantId = GetTenantId();
 | 
			
		||||
            var currentEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            var batchId = Guid.NewGuid();
 | 
			
		||||
 | 
			
		||||
            using var transaction = await _context.Database.BeginTransactionAsync();
 | 
			
		||||
            try
 | 
			
		||||
@ -704,10 +705,12 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
                    document = new Document
 | 
			
		||||
                    {
 | 
			
		||||
                        BatchId = batchId,
 | 
			
		||||
                        UploadedById = loggedInEmployee.Id,
 | 
			
		||||
                        FileName = recordAttendanceDot.Image.FileName ?? "",
 | 
			
		||||
                        ContentType = recordAttendanceDot.Image.ContentType,
 | 
			
		||||
                        S3Key = objectKey,
 | 
			
		||||
                        Base64Data = recordAttendanceDot.Image.Base64Data,
 | 
			
		||||
                        //Base64Data = recordAttendanceDot.Image.Base64Data,
 | 
			
		||||
                        FileSize = recordAttendanceDot.Image.FileSize,
 | 
			
		||||
                        UploadedAt = recordAttendanceDot.Date,
 | 
			
		||||
                        TenantId = tenantId
 | 
			
		||||
@ -728,7 +731,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                    Longitude = recordAttendanceDot.Longitude,
 | 
			
		||||
                    DocumentId = document?.Id,
 | 
			
		||||
                    TenantId = tenantId,
 | 
			
		||||
                    UpdatedBy = recordAttendanceDot.EmployeeID,
 | 
			
		||||
                    UpdatedBy = loggedInEmployee.Id,
 | 
			
		||||
                    UpdatedOn = recordAttendanceDot.Date
 | 
			
		||||
                };
 | 
			
		||||
                _context.AttendanceLogs.Add(attendanceLog);
 | 
			
		||||
@ -755,7 +758,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
                var notification = new
 | 
			
		||||
                {
 | 
			
		||||
                    LoggedInUserId = currentEmployee.Id,
 | 
			
		||||
                    LoggedInUserId = loggedInEmployee.Id,
 | 
			
		||||
                    Keyword = "Attendance",
 | 
			
		||||
                    Activity = recordAttendanceDot.Id == Guid.Empty ? 1 : 0,
 | 
			
		||||
                    ProjectId = attendance.ProjectID,
 | 
			
		||||
 | 
			
		||||
@ -48,6 +48,8 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
 | 
			
		||||
            }
 | 
			
		||||
            Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            var batchId = Guid.NewGuid();
 | 
			
		||||
            TicketForum ticketForum = createTicketDto.ToTicketForumFromCreateTicketDto(tenantId);
 | 
			
		||||
            _context.Tickets.Add(ticketForum);
 | 
			
		||||
            await _context.SaveChangesAsync();
 | 
			
		||||
@ -79,7 +81,7 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                    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, batchId, loggedInEmployee.Id);
 | 
			
		||||
                    _context.Documents.Add(document);
 | 
			
		||||
                    await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
@ -162,7 +164,15 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
 | 
			
		||||
            }
 | 
			
		||||
            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)
 | 
			
		||||
            {
 | 
			
		||||
                TicketForum ticketForum = updateTicketDto.ToTicketForumFromUpdateTicketDto(existingTicket);
 | 
			
		||||
@ -202,7 +212,7 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                            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, batchId, loggedInEmployee.Id);
 | 
			
		||||
                            _context.Documents.Add(document);
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
@ -344,6 +354,9 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            var batchId = Guid.NewGuid();
 | 
			
		||||
 | 
			
		||||
            List<TicketAttachment> attachments = new List<TicketAttachment>();
 | 
			
		||||
            List<Document> documents = new List<Document>();
 | 
			
		||||
 | 
			
		||||
@ -381,7 +394,7 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                    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, batchId, loggedInEmployee.Id);
 | 
			
		||||
                    _context.Documents.Add(document);
 | 
			
		||||
                    await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
@ -429,6 +442,9 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            var batchId = Guid.NewGuid();
 | 
			
		||||
 | 
			
		||||
            List<TicketAttachment> attachments = new List<TicketAttachment>();
 | 
			
		||||
 | 
			
		||||
            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}";
 | 
			
		||||
                        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);
 | 
			
		||||
                        await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
@ -541,6 +557,9 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            var batchId = Guid.NewGuid();
 | 
			
		||||
 | 
			
		||||
            List<TicketAttachmentVM> ticketAttachmentVMs = new List<TicketAttachmentVM>();
 | 
			
		||||
 | 
			
		||||
            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}";
 | 
			
		||||
                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);
 | 
			
		||||
                await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										315
									
								
								Marco.Pms.Services/Controllers/ImageController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								Marco.Pms.Services/Controllers/ImageController.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,315 @@
 | 
			
		||||
using Marco.Pms.DataAccess.Data;
 | 
			
		||||
using Marco.Pms.Model.Activities;
 | 
			
		||||
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.Mvc;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.Services.Controllers
 | 
			
		||||
{
 | 
			
		||||
    [Route("api/[controller]")]
 | 
			
		||||
    [ApiController]
 | 
			
		||||
    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)
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogInfo("GetImageList called 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("Project not found for ProjectId: {ProjectId}", projectId);
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 400));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Step 2: Check permission
 | 
			
		||||
            var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId.ToString());
 | 
			
		||||
            if (!hasPermission)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogWarning("No access to ProjectId: {ProjectId} for EmployeeId: {EmployeeId}", projectId, loggedInEmployee.Id);
 | 
			
		||||
                return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "You don't have access", 403));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Step 3: Fetch building > floor > work area > work item hierarchy
 | 
			
		||||
            var buildings = await _context.Buildings
 | 
			
		||||
                .Where(b => b.ProjectId == projectId)
 | 
			
		||||
                .Select(b => new { b.Id, b.Name })
 | 
			
		||||
                .ToListAsync();
 | 
			
		||||
 | 
			
		||||
            var buildingIds = buildings.Select(b => b.Id).ToList();
 | 
			
		||||
 | 
			
		||||
            var floors = await _context.Floor
 | 
			
		||||
                .Where(f => buildingIds.Contains(f.BuildingId))
 | 
			
		||||
                .Select(f => new { f.Id, f.BuildingId, f.FloorName })
 | 
			
		||||
                .ToListAsync();
 | 
			
		||||
 | 
			
		||||
            var floorIds = floors.Select(f => f.Id).ToList();
 | 
			
		||||
 | 
			
		||||
            var workAreas = await _context.WorkAreas
 | 
			
		||||
                .Where(wa => floorIds.Contains(wa.FloorId))
 | 
			
		||||
                .Select(wa => new { wa.Id, wa.FloorId, wa.AreaName })
 | 
			
		||||
                .ToListAsync();
 | 
			
		||||
 | 
			
		||||
            var workAreaIds = workAreas.Select(wa => wa.Id).ToList();
 | 
			
		||||
 | 
			
		||||
            var workItems = await _context.WorkItems
 | 
			
		||||
                .Include(wi => wi.ActivityMaster)
 | 
			
		||||
                .Where(wi => workAreaIds.Contains(wi.WorkAreaId))
 | 
			
		||||
                .Select(wi => new { wi.Id, wi.WorkAreaId, wi.ActivityMaster })
 | 
			
		||||
                .ToListAsync();
 | 
			
		||||
 | 
			
		||||
            var workItemIds = workItems.Select(wi => wi.Id).ToList();
 | 
			
		||||
 | 
			
		||||
            // Step 4: Fetch task and comment data
 | 
			
		||||
            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();
 | 
			
		||||
 | 
			
		||||
            // Step 5: Fetch attachments and related documents
 | 
			
		||||
            var attachments = await _context.TaskAttachments
 | 
			
		||||
                .Where(ta => taskIds.Contains(ta.ReferenceId) || commentIds.Contains(ta.ReferenceId))
 | 
			
		||||
                .ToListAsync();
 | 
			
		||||
 | 
			
		||||
            var documentIds = attachments.Select(ta => ta.DocumentId).ToList();
 | 
			
		||||
 | 
			
		||||
            var documents = await _context.Documents
 | 
			
		||||
                .Include(d => d.UploadedBy)
 | 
			
		||||
                .Where(d => documentIds.Contains(d.Id))
 | 
			
		||||
                .ToListAsync();
 | 
			
		||||
 | 
			
		||||
            // Step 6: Prepare view models
 | 
			
		||||
            var documentVM = documents
 | 
			
		||||
                .Select(d =>
 | 
			
		||||
                {
 | 
			
		||||
                    var referenceId = attachments
 | 
			
		||||
                        .Where(ta => ta.DocumentId == d.Id)
 | 
			
		||||
                        .Select(ta => ta.ReferenceId)
 | 
			
		||||
                        .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
                    var task = tasks.FirstOrDefault(t => t.Id == referenceId);
 | 
			
		||||
                    var comment = comments.FirstOrDefault(c => c.Id == referenceId);
 | 
			
		||||
 | 
			
		||||
                    string source = "";
 | 
			
		||||
                    Employee? uploadedBy = null;
 | 
			
		||||
                    if (task != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        uploadedBy = task.ReportedBy;
 | 
			
		||||
                        source = "Report";
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (comment != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        task = tasks.FirstOrDefault(t => t.Id == comment.TaskAllocationId);
 | 
			
		||||
                        uploadedBy = comment.Employee;
 | 
			
		||||
                        source = "Comment";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var workItem = workItems.FirstOrDefault(wi => wi.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
 | 
			
		||||
                    {
 | 
			
		||||
                        Id = d.Id,
 | 
			
		||||
                        BatchId = d.BatchId,
 | 
			
		||||
                        thumbnailUrl = d.ThumbS3Key != null ? _s3Service.GeneratePreSignedUrlAsync(d.ThumbS3Key) : (d.S3Key != null ? _s3Service.GeneratePreSignedUrlAsync(d.S3Key) : null),
 | 
			
		||||
                        ImageUrl = d.S3Key != null ? _s3Service.GeneratePreSignedUrlAsync(d.S3Key) : null,
 | 
			
		||||
                        UploadedBy = d.UploadedBy?.ToBasicEmployeeVMFromEmployee() ?? uploadedBy?.ToBasicEmployeeVMFromEmployee(),
 | 
			
		||||
                        UploadedAt = d.UploadedAt,
 | 
			
		||||
                        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,
 | 
			
		||||
                        ActivityName = workItem?.ActivityMaster?.ActivityName,
 | 
			
		||||
                        CommentId = comment?.Id,
 | 
			
		||||
                        Comment = comment?.Comment
 | 
			
		||||
                    };
 | 
			
		||||
                }).ToList();
 | 
			
		||||
 | 
			
		||||
            _logger.LogInfo("Image list fetched for ProjectId: {ProjectId}. Total documents: {Count}", projectId, documentVM.Count);
 | 
			
		||||
            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)
 | 
			
		||||
                    .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)
 | 
			
		||||
                    .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 = documents.Select(d => new
 | 
			
		||||
            {
 | 
			
		||||
                Id = d.Id,
 | 
			
		||||
                BatchId = d.BatchId,
 | 
			
		||||
                thumbnailUrl = d.ThumbS3Key != null
 | 
			
		||||
                    ? _s3Service.GeneratePreSignedUrlAsync(d.ThumbS3Key)
 | 
			
		||||
                    : (d.S3Key != null ? _s3Service.GeneratePreSignedUrlAsync(d.S3Key) : null),
 | 
			
		||||
                ImageUrl = d.S3Key != null ? _s3Service.GeneratePreSignedUrlAsync(d.S3Key) : null,
 | 
			
		||||
                UploadedBy = d.UploadedBy?.ToBasicEmployeeVMFromEmployee() ?? uploadedBy?.ToBasicEmployeeVMFromEmployee(),
 | 
			
		||||
                UploadedAt = d.UploadedAt,
 | 
			
		||||
                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,
 | 
			
		||||
                ActivityName = workItem?.ActivityMaster?.ActivityName,
 | 
			
		||||
                CommentId = comment?.Id,
 | 
			
		||||
                Comment = comment?.Comment
 | 
			
		||||
            }).ToList();
 | 
			
		||||
 | 
			
		||||
            _logger.LogInfo("Fetched {Count} image(s) for BatchId: {BatchId}", response.Count, 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));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -204,6 +204,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
                var building = await _context.Buildings
 | 
			
		||||
                    .FirstOrDefaultAsync(b => b.Id == buildingId);
 | 
			
		||||
                var batchId = Guid.NewGuid();
 | 
			
		||||
 | 
			
		||||
                foreach (var image in reportTask.Images)
 | 
			
		||||
                {
 | 
			
		||||
@ -225,10 +226,12 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
                    var document = new Document
 | 
			
		||||
                    {
 | 
			
		||||
                        BatchId = batchId,
 | 
			
		||||
                        UploadedById = loggedInEmployee.Id,
 | 
			
		||||
                        FileName = image.FileName ?? "",
 | 
			
		||||
                        ContentType = image.ContentType ?? "",
 | 
			
		||||
                        S3Key = objectKey,
 | 
			
		||||
                        Base64Data = image.Base64Data,
 | 
			
		||||
                        //Base64Data = image.Base64Data,
 | 
			
		||||
                        FileSize = image.FileSize,
 | 
			
		||||
                        UploadedAt = DateTime.UtcNow,
 | 
			
		||||
                        TenantId = tenantId
 | 
			
		||||
@ -265,7 +268,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            _logger.LogInfo("AddCommentForTask called for TaskAllocationId: {TaskId}", createComment.TaskAllocationId);
 | 
			
		||||
 | 
			
		||||
            var tenantId = GetTenantId();
 | 
			
		||||
            var employee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
 | 
			
		||||
            // Validate Task Allocation and associated WorkItem
 | 
			
		||||
            var taskAllocation = await _context.TaskAllocations
 | 
			
		||||
@ -287,13 +290,14 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            var building = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == buildingId);
 | 
			
		||||
 | 
			
		||||
            // Save comment
 | 
			
		||||
            var comment = createComment.ToCommentFromCommentDto(tenantId, employee.Id);
 | 
			
		||||
            var comment = createComment.ToCommentFromCommentDto(tenantId, loggedInEmployee.Id);
 | 
			
		||||
            _context.TaskComments.Add(comment);
 | 
			
		||||
            await _context.SaveChangesAsync();
 | 
			
		||||
            _logger.LogInfo("Comment saved with Id: {CommentId}", comment.Id);
 | 
			
		||||
 | 
			
		||||
            // Process image uploads
 | 
			
		||||
            var images = createComment.Images;
 | 
			
		||||
            var batchId = Guid.NewGuid();
 | 
			
		||||
 | 
			
		||||
            if (images != null && images.Any())
 | 
			
		||||
            {
 | 
			
		||||
@ -319,10 +323,12 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
                    var document = new Document
 | 
			
		||||
                    {
 | 
			
		||||
                        BatchId = batchId,
 | 
			
		||||
                        UploadedById = loggedInEmployee.Id,
 | 
			
		||||
                        FileName = image.FileName ?? string.Empty,
 | 
			
		||||
                        ContentType = image.ContentType ?? fileType,
 | 
			
		||||
                        S3Key = objectKey,
 | 
			
		||||
                        Base64Data = image.Base64Data,
 | 
			
		||||
                        //Base64Data = image.Base64Data,
 | 
			
		||||
                        FileSize = image.FileSize,
 | 
			
		||||
                        UploadedAt = DateTime.UtcNow,
 | 
			
		||||
                        TenantId = tenantId
 | 
			
		||||
@ -731,6 +737,8 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                var building = await _context.Buildings
 | 
			
		||||
                    .FirstOrDefaultAsync(b => b.Id == buildingId);
 | 
			
		||||
 | 
			
		||||
                var batchId = Guid.NewGuid();
 | 
			
		||||
 | 
			
		||||
                foreach (var image in approveTask.Images)
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.IsNullOrEmpty(image.Base64Data))
 | 
			
		||||
@ -749,10 +757,12 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
                    var document = new Document
 | 
			
		||||
                    {
 | 
			
		||||
                        BatchId = batchId,
 | 
			
		||||
                        UploadedById = loggedInEmployee.Id,
 | 
			
		||||
                        FileName = fileName,
 | 
			
		||||
                        ContentType = image.ContentType ?? string.Empty,
 | 
			
		||||
                        S3Key = objectKey,
 | 
			
		||||
                        Base64Data = image.Base64Data,
 | 
			
		||||
                        //Base64Data = image.Base64Data,
 | 
			
		||||
                        FileSize = image.FileSize,
 | 
			
		||||
                        UploadedAt = DateTime.UtcNow,
 | 
			
		||||
                        TenantId = tenantId
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user