Compare commits
	
		
			No commits in common. "2ed0e6e5b6608765bd297350f6e8251178c80355" and "cb2856b2df205b4d2a09494691a3affa29ef5149" have entirely different histories.
		
	
	
		
			2ed0e6e5b6
			...
			cb2856b2df
		
	
		
@ -44,7 +44,6 @@ namespace Marco.Pms.DataAccess.Data
 | 
			
		||||
        public DbSet<TaskAllocation> TaskAllocations { get; set; }
 | 
			
		||||
        public DbSet<TaskComment> TaskComments { get; set; }
 | 
			
		||||
        public DbSet<TaskMembers> TaskMembers { get; set; }
 | 
			
		||||
        public DbSet<TaskAttachment> TaskAttachments { get; set; }
 | 
			
		||||
        public DbSet<Attendance> Attendes { get; set; }
 | 
			
		||||
        public DbSet<AttendanceLog> AttendanceLogs { get; set; }
 | 
			
		||||
        public DbSet<Employee> Employees { get; set; }
 | 
			
		||||
@ -160,20 +159,15 @@ namespace Marco.Pms.DataAccess.Data
 | 
			
		||||
                    TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                },
 | 
			
		||||
                 new StatusMaster
 | 
			
		||||
                 {
 | 
			
		||||
                     Id = new Guid("cdad86aa-8a56-4ff4-b633-9c629057dfef"),
 | 
			
		||||
                     Status = "In Progress",
 | 
			
		||||
                     TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                 }, new StatusMaster
 | 
			
		||||
                 {
 | 
			
		||||
                     Id = new Guid("603e994b-a27f-4e5d-a251-f3d69b0498ba"),
 | 
			
		||||
                     Status = "On Hold",
 | 
			
		||||
                     Status = "In Progress",
 | 
			
		||||
                     TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                 },
 | 
			
		||||
                  new StatusMaster
 | 
			
		||||
                  {
 | 
			
		||||
                      Id = new Guid("ef1c356e-0fe0-42df-a5d3-8daee355492d"),
 | 
			
		||||
                      Status = "In Active",
 | 
			
		||||
                      Status = "On Hold",
 | 
			
		||||
                      TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                  },
 | 
			
		||||
                   new StatusMaster
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,36 +0,0 @@
 | 
			
		||||
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");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,57 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public partial class Added_New_Status_Master_In_Progress : Migration
 | 
			
		||||
    {
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.UpdateData(
 | 
			
		||||
                table: "StatusMasters",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("603e994b-a27f-4e5d-a251-f3d69b0498ba"),
 | 
			
		||||
                column: "Status",
 | 
			
		||||
                value: "On Hold");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.UpdateData(
 | 
			
		||||
                table: "StatusMasters",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("ef1c356e-0fe0-42df-a5d3-8daee355492d"),
 | 
			
		||||
                column: "Status",
 | 
			
		||||
                value: "In Active");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.InsertData(
 | 
			
		||||
                table: "StatusMasters",
 | 
			
		||||
                columns: new[] { "Id", "Status", "TenantId" },
 | 
			
		||||
                values: new object[] { new Guid("cdad86aa-8a56-4ff4-b633-9c629057dfef"), "In Progress", new Guid("b3466e83-7e11-464c-b93a-daf047838b26") });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "StatusMasters",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("cdad86aa-8a56-4ff4-b633-9c629057dfef"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.UpdateData(
 | 
			
		||||
                table: "StatusMasters",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("603e994b-a27f-4e5d-a251-f3d69b0498ba"),
 | 
			
		||||
                column: "Status",
 | 
			
		||||
                value: "In Progress");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.UpdateData(
 | 
			
		||||
                table: "StatusMasters",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("ef1c356e-0fe0-42df-a5d3-8daee355492d"),
 | 
			
		||||
                column: "Status",
 | 
			
		||||
                value: "On Hold");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -63,23 +63,6 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                    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 =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<Guid>("Id")
 | 
			
		||||
@ -1675,20 +1658,14 @@ namespace Marco.Pms.DataAccess.Migrations
 | 
			
		||||
                        },
 | 
			
		||||
                        new
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = new Guid("cdad86aa-8a56-4ff4-b633-9c629057dfef"),
 | 
			
		||||
                            Id = new Guid("603e994b-a27f-4e5d-a251-f3d69b0498ba"),
 | 
			
		||||
                            Status = "In Progress",
 | 
			
		||||
                            TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                        },
 | 
			
		||||
                        new
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = new Guid("603e994b-a27f-4e5d-a251-f3d69b0498ba"),
 | 
			
		||||
                            Status = "On Hold",
 | 
			
		||||
                            TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                        },
 | 
			
		||||
                        new
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = new Guid("ef1c356e-0fe0-42df-a5d3-8daee355492d"),
 | 
			
		||||
                            Status = "In Active",
 | 
			
		||||
                            Status = "On Hold",
 | 
			
		||||
                            TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
 | 
			
		||||
                        },
 | 
			
		||||
                        new
 | 
			
		||||
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
namespace Marco.Pms.Model.Activities
 | 
			
		||||
{
 | 
			
		||||
    public class TaskAttachment
 | 
			
		||||
    {
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public Guid ReferenceId { get; set; }
 | 
			
		||||
        public Guid DocumentId { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								Marco.Pms.Model/Activities/TaskImages.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Marco.Pms.Model/Activities/TaskImages.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +1,11 @@
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.Model.Dtos.Activities
 | 
			
		||||
namespace Marco.Pms.Model.Dtos.Activities
 | 
			
		||||
{
 | 
			
		||||
    public class CreateCommentDto
 | 
			
		||||
    {
 | 
			
		||||
        public Guid TaskAllocationId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public DateTime CommentDate { get; set; }
 | 
			
		||||
        public string? Comment { get; set; }
 | 
			
		||||
        public List<FileUploadModel>? Images { get; set; }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,4 @@
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.Model.Dtos.Activities
 | 
			
		||||
namespace Marco.Pms.Model.Dtos.Activities
 | 
			
		||||
{
 | 
			
		||||
    public class ReportTaskDto
 | 
			
		||||
    {
 | 
			
		||||
@ -9,6 +7,5 @@ namespace Marco.Pms.Model.Dtos.Activities
 | 
			
		||||
        public DateTime ReportedDate { get; set; }
 | 
			
		||||
        public string? Comment { get; set; }
 | 
			
		||||
        public List<ReportCheckListDto>? CheckList { get; set; }
 | 
			
		||||
        public List<FileUploadModel>? Images { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@
 | 
			
		||||
        public List<UpdateContactPhoneDto>? ContactPhones { get; set; }
 | 
			
		||||
        public List<UpdateContactEmailDto>? ContactEmails { 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? Organization { get; set; }
 | 
			
		||||
        public string? Address { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -101,7 +101,6 @@ namespace Marco.Pms.Model.Mapper
 | 
			
		||||
            {
 | 
			
		||||
                Id = taskAllocation.Id,
 | 
			
		||||
                AssignmentDate = taskAllocation.AssignmentDate,
 | 
			
		||||
                Description = taskAllocation.Description,
 | 
			
		||||
                PlannedTask = taskAllocation.PlannedTask,
 | 
			
		||||
                ReportedDate = taskAllocation.ReportedDate,
 | 
			
		||||
                CompletedTask = taskAllocation.CompletedTask,
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,5 @@
 | 
			
		||||
        public string? Comment { get; set; }
 | 
			
		||||
        public Guid CommentedBy { get; set; }
 | 
			
		||||
        public BasicEmployeeVM? Employee { get; set; }
 | 
			
		||||
        public List<string>? PreSignedUrls { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,7 @@ namespace Marco.Pms.Model.ViewModels.Activities
 | 
			
		||||
        public double PlannedTask { get; set; }
 | 
			
		||||
        public double CompletedTask { get; set; }
 | 
			
		||||
        public BasicEmployeeVM? AssignedBy { get; set; }
 | 
			
		||||
        public string? Description { get; set; }
 | 
			
		||||
        public Guid WorkItemId { get; set; }
 | 
			
		||||
        public List<string>? ReportedPreSignedUrls { get; set; }
 | 
			
		||||
        public WorkItem? WorkItem { get; set; }
 | 
			
		||||
        public List<BasicEmployeeVM>? teamMembers { get; set; }
 | 
			
		||||
        public List<CommentVM>? comments { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@
 | 
			
		||||
        public string? Description { get; set; }
 | 
			
		||||
        public Guid AssignedBy { get; set; }
 | 
			
		||||
        public Guid WorkItemId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public List<CommentVM>? Comments { get; set; }
 | 
			
		||||
        public List<CheckListVM>? checkList { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,6 @@ namespace Marco.Pms.Model.ViewModels.Activities
 | 
			
		||||
        public string? Description { get; set; }
 | 
			
		||||
        public string? AssignBy { get; set; }
 | 
			
		||||
        public WorkItem? WorkItem { get; set; }
 | 
			
		||||
        public List<string>? PreSignedUrls { get; set; }
 | 
			
		||||
        public List<CommentVM>? Comments { get; set; }
 | 
			
		||||
        public List<EmployeeVM>? TeamMembers { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -7,13 +7,11 @@ using Marco.Pms.Model.Mapper;
 | 
			
		||||
using Marco.Pms.Model.Projects;
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
using Marco.Pms.Model.ViewModels.AttendanceVM;
 | 
			
		||||
using Marco.Pms.Services.Hubs;
 | 
			
		||||
using Marco.Pms.Services.Service;
 | 
			
		||||
using MarcoBMS.Services.Helpers;
 | 
			
		||||
using MarcoBMS.Services.Service;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.SignalR;
 | 
			
		||||
using Microsoft.CodeAnalysis;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Document = Marco.Pms.Model.DocumentManager.Document;
 | 
			
		||||
@ -32,11 +30,10 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        private readonly S3UploadService _s3Service;
 | 
			
		||||
        private readonly PermissionServices _permission;
 | 
			
		||||
        private readonly ILoggingService _logger;
 | 
			
		||||
        private readonly IHubContext<MarcoHub> _signalR;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AttendanceController(
 | 
			
		||||
             ApplicationDbContext context, EmployeeHelper employeeHelper, ProjectsHelper projectsHelper, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permission, IHubContext<MarcoHub> signalR)
 | 
			
		||||
             ApplicationDbContext context, EmployeeHelper employeeHelper, ProjectsHelper projectsHelper, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permission)
 | 
			
		||||
        {
 | 
			
		||||
            _context = context;
 | 
			
		||||
            _employeeHelper = employeeHelper;
 | 
			
		||||
@ -45,7 +42,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            _s3Service = s3Service;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
            _permission = permission;
 | 
			
		||||
            _signalR = signalR;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Guid GetTenantId()
 | 
			
		||||
@ -562,13 +558,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                        Activity = attendance.Activity,
 | 
			
		||||
                        JobRoleName = employee.JobRole.Name
 | 
			
		||||
                    };
 | 
			
		||||
                    var sendActivity = 0;
 | 
			
		||||
                    if (recordAttendanceDot.Id == Guid.Empty)
 | 
			
		||||
                    {
 | 
			
		||||
                        sendActivity = 1;
 | 
			
		||||
                    }
 | 
			
		||||
                    var notification = new { LoggedInUserId = currentEmployee.Id, Keyword = "Attendance", Activity = sendActivity, ProjectId = attendance.ProjectID, Response = vm };
 | 
			
		||||
                    await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
                    _logger.LogInfo("Attendance for employee {FirstName} {LastName} has been marked", employee.FirstName ?? string.Empty, employee.LastName ?? string.Empty);
 | 
			
		||||
                    return Ok(ApiResponse<object>.SuccessResponse(vm, "Attendance marked successfully.", 200));
 | 
			
		||||
                }
 | 
			
		||||
@ -699,6 +688,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                }
 | 
			
		||||
                Document? document = null;
 | 
			
		||||
                var Image = recordAttendanceDot.Image;
 | 
			
		||||
                var objectKey = string.Empty;
 | 
			
		||||
                var preSignedUrl = string.Empty;
 | 
			
		||||
 | 
			
		||||
                if (Image != null && Image.ContentType != null)
 | 
			
		||||
@ -707,16 +697,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                    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, "attendance");
 | 
			
		||||
 | 
			
		||||
                    string objectKey = $"tenant-{TenantId}/Employee/{recordAttendanceDot.EmployeeID}/Attendance/{fileName}";
 | 
			
		||||
                    await _s3Service.UploadFileAsync(base64, fileType, objectKey);
 | 
			
		||||
                    objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, TenantId, "attendance");
 | 
			
		||||
                    preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(objectKey);
 | 
			
		||||
 | 
			
		||||
                    document = new Document
 | 
			
		||||
@ -731,7 +712,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                    };
 | 
			
		||||
                    _context.Documents.Add(document);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    await _context.SaveChangesAsync();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,13 +9,11 @@ using Marco.Pms.Model.Mapper;
 | 
			
		||||
using Marco.Pms.Model.Projects;
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
using Marco.Pms.Model.ViewModels.Employee;
 | 
			
		||||
using Marco.Pms.Services.Hubs;
 | 
			
		||||
using MarcoBMS.Services.Helpers;
 | 
			
		||||
using MarcoBMS.Services.Service;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Identity;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.SignalR;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
 | 
			
		||||
namespace MarcoBMS.Services.Controllers
 | 
			
		||||
@ -34,11 +32,9 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        private readonly UserHelper _userHelper;
 | 
			
		||||
        private readonly IConfiguration _configuration;
 | 
			
		||||
        private readonly ILoggingService _logger;
 | 
			
		||||
        private readonly IHubContext<MarcoHub> _signalR;
 | 
			
		||||
 | 
			
		||||
        public EmployeeController(UserManager<ApplicationUser> userManager, IEmailSender emailSender,
 | 
			
		||||
            ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger,
 | 
			
		||||
            IHubContext<MarcoHub> signalR)
 | 
			
		||||
            ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger)
 | 
			
		||||
        {
 | 
			
		||||
            _context = context;
 | 
			
		||||
            _userManager = userManager;
 | 
			
		||||
@ -47,7 +43,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            _userHelper = userHelper;
 | 
			
		||||
            _configuration = configuration;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
            _signalR = signalR;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpGet]
 | 
			
		||||
@ -159,8 +154,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        public async Task<IActionResult> CreateUser([FromBody] CreateUserDto model)
 | 
			
		||||
        {
 | 
			
		||||
            Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            Guid employeeId = Guid.Empty;
 | 
			
		||||
            if (model == null)
 | 
			
		||||
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", "Invaild Data", 400));
 | 
			
		||||
 | 
			
		||||
@ -187,7 +180,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
                        _context.Employees.Update(existingEmployee);
 | 
			
		||||
                        await _context.SaveChangesAsync();
 | 
			
		||||
                        employeeId = existingEmployee.Id;
 | 
			
		||||
                        responsemessage = "User updated successfully.";
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
@ -222,7 +214,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                        _context.Employees.Add(newEmployee);
 | 
			
		||||
 | 
			
		||||
                        await _context.SaveChangesAsync();
 | 
			
		||||
                        employeeId = newEmployee.Id;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        /* SEND USER REGISTRATION MAIL*/
 | 
			
		||||
                        var token = await _userManager.GeneratePasswordResetTokenAsync(user);
 | 
			
		||||
@ -241,7 +233,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                        _context.Employees.Update(existingEmployee);
 | 
			
		||||
                        await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
                        employeeId = existingEmployee.Id;
 | 
			
		||||
 | 
			
		||||
                        /* SEND USER REGISTRATION MAIL*/
 | 
			
		||||
                        var token = await _userManager.GeneratePasswordResetTokenAsync(user);
 | 
			
		||||
@ -265,22 +256,17 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                    existingEmployee = GetUpdateEmployeeModel(model, existingEmployee);
 | 
			
		||||
                    _context.Employees.Update(existingEmployee);
 | 
			
		||||
                    responsemessage = "User updated successfully.";
 | 
			
		||||
                    employeeId = existingEmployee.Id;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    // Create Employee record if missing
 | 
			
		||||
                    Employee newEmployee = GetNewEmployeeModel(model, tenantId, string.Empty);
 | 
			
		||||
                    _context.Employees.Add(newEmployee);
 | 
			
		||||
                    employeeId = newEmployee.Id;
 | 
			
		||||
                }
 | 
			
		||||
                await _context.SaveChangesAsync();
 | 
			
		||||
                responsemessage = "User created successfully.";
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Employee", EmployeeId = employeeId };
 | 
			
		||||
 | 
			
		||||
            await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
            return Ok(ApiResponse<object>.SuccessResponse("Success.", responsemessage, 200));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -434,9 +420,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                    }
 | 
			
		||||
                    await _context.SaveChangesAsync();
 | 
			
		||||
                    _logger.LogInfo("Employee with ID {EmployeId} Deleted successfully", employee.Id);
 | 
			
		||||
                    var notification = new { LoggedInUserId = LoggedEmployee.Id, Keyword = "Employee", EmployeeId = employee.Id };
 | 
			
		||||
 | 
			
		||||
                    await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
 | 
			
		||||
@ -68,16 +68,7 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                        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, "forum");
 | 
			
		||||
 | 
			
		||||
                    string objectKey = $"tenant-{tenantId}/project-{createTicketDto.LinkedProjectId}/froum/{fileName}";
 | 
			
		||||
                    await _s3Service.UploadFileAsync(base64, fileType, objectKey);
 | 
			
		||||
                    var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "forum");
 | 
			
		||||
 | 
			
		||||
                    Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, createTicketDto.CreatedAt, tenantId);
 | 
			
		||||
                    _context.Documents.Add(document);
 | 
			
		||||
@ -191,16 +182,7 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                                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, "forum");
 | 
			
		||||
 | 
			
		||||
                            string objectKey = $"tenant-{tenantId}/project-{updateTicketDto.LinkedProjectId}/froum/{fileName}";
 | 
			
		||||
                            await _s3Service.UploadFileAsync(base64, fileType, objectKey);
 | 
			
		||||
                            var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "forum");
 | 
			
		||||
 | 
			
		||||
                            Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, updateTicketDto.CreatedAt, tenantId);
 | 
			
		||||
                            _context.Documents.Add(document);
 | 
			
		||||
@ -347,14 +329,6 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
            List<TicketAttachment> attachments = new List<TicketAttachment>();
 | 
			
		||||
            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);
 | 
			
		||||
            _context.TicketComments.Add(comment);
 | 
			
		||||
            await _context.SaveChangesAsync();
 | 
			
		||||
@ -370,16 +344,7 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                        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, "forum");
 | 
			
		||||
 | 
			
		||||
                    string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}";
 | 
			
		||||
                    await _s3Service.UploadFileAsync(base64, fileType, objectKey);
 | 
			
		||||
                    var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "forum");
 | 
			
		||||
 | 
			
		||||
                    Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, addCommentDto.SentAt, tenantId);
 | 
			
		||||
                    _context.Documents.Add(document);
 | 
			
		||||
@ -431,14 +396,6 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
            Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
            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 updateComment = updateCommentDto.ToTicketCommentFromUpdateCommentDto(tenantId, existingComment);
 | 
			
		||||
            _context.TicketComments.Update(updateComment);
 | 
			
		||||
@ -462,16 +419,7 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                            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, "forum");
 | 
			
		||||
 | 
			
		||||
                        string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}";
 | 
			
		||||
                        await _s3Service.UploadFileAsync(base64, fileType, objectKey);
 | 
			
		||||
                        var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "forum");
 | 
			
		||||
 | 
			
		||||
                        Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, existingComment.SentAt, tenantId);
 | 
			
		||||
                        _context.Documents.Add(document);
 | 
			
		||||
@ -543,16 +491,6 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
            Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
            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();
 | 
			
		||||
 | 
			
		||||
            foreach (var forumAttachmentDto in forumAttachmentDtos)
 | 
			
		||||
@ -567,17 +505,7 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
                    _logger.LogError("ticket ID is missing");
 | 
			
		||||
                    return BadRequest(ApiResponse<object>.ErrorResponse("ticket ID is missing", "ticket ID is missing", 400));
 | 
			
		||||
                }
 | 
			
		||||
                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);
 | 
			
		||||
                var objectKey = await _s3Service.UploadFileAsync(forumAttachmentDto.Base64Data, tenantId, "forum");
 | 
			
		||||
 | 
			
		||||
                Document document = forumAttachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, forumAttachmentDto.SentAt, tenantId);
 | 
			
		||||
                _context.Documents.Add(document);
 | 
			
		||||
 | 
			
		||||
@ -749,11 +749,11 @@ namespace Marco.Pms.Services.Controllers
 | 
			
		||||
            return Ok(response);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //[HttpGet("contact-tag/{id}")]
 | 
			
		||||
        //public async Task<IActionResult> GetContactTagMaster(Guid id)
 | 
			
		||||
        //{
 | 
			
		||||
        //    return Ok();
 | 
			
		||||
        //}
 | 
			
		||||
        [HttpGet("contact-tag/{id}")]
 | 
			
		||||
        public async Task<IActionResult> GetContactTagMaster(Guid id)
 | 
			
		||||
        {
 | 
			
		||||
            return Ok();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpPost("contact-tag")]
 | 
			
		||||
        public async Task<IActionResult> CreateContactTagMaster([FromBody] CreateContactTagDto contactTagDto)
 | 
			
		||||
 | 
			
		||||
@ -8,13 +8,12 @@ using Marco.Pms.Model.Projects;
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
using Marco.Pms.Model.ViewModels.Employee;
 | 
			
		||||
using Marco.Pms.Model.ViewModels.Projects;
 | 
			
		||||
using Marco.Pms.Services.Hubs;
 | 
			
		||||
using MarcoBMS.Services.Helpers;
 | 
			
		||||
using MarcoBMS.Services.Service;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.SignalR;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
 | 
			
		||||
namespace MarcoBMS.Services.Controllers
 | 
			
		||||
{
 | 
			
		||||
@ -28,18 +27,15 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        private readonly ILoggingService _logger;
 | 
			
		||||
        private readonly RolesHelper _rolesHelper;
 | 
			
		||||
        private readonly ProjectsHelper _projectsHelper;
 | 
			
		||||
        private readonly IHubContext<MarcoHub> _signalR;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public ProjectController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, RolesHelper rolesHelper, ProjectsHelper projectHelper, IHubContext<MarcoHub> signalR)
 | 
			
		||||
        public ProjectController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, RolesHelper rolesHelper, ProjectsHelper projectHelper)
 | 
			
		||||
        {
 | 
			
		||||
            _context = context;
 | 
			
		||||
            _userHelper = userHelper;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
            _rolesHelper = rolesHelper;
 | 
			
		||||
            _projectsHelper = projectHelper;
 | 
			
		||||
            _signalR = signalR;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpGet("list/basic")]
 | 
			
		||||
@ -63,9 +59,9 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                return Unauthorized(ApiResponse<object>.ErrorResponse("Employee not found.", null, 401));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            List<Project> projects = await _projectsHelper.GetMyProjects(tenantId, LoggedInEmployee);
 | 
			
		||||
 | 
			
		||||
           
 | 
			
		||||
            List<Project> projects =  await _projectsHelper.GetMyProjects(tenantId, LoggedInEmployee);
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            // 4. Project projection to ProjectInfoVM
 | 
			
		||||
            // This part is already quite efficient.
 | 
			
		||||
@ -88,6 +84,8 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            return Ok(ApiResponse<object>.SuccessResponse(response, "Success.", 200));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
     
 | 
			
		||||
 | 
			
		||||
        [HttpGet("list")]
 | 
			
		||||
        public async Task<IActionResult> GetAll()
 | 
			
		||||
        {
 | 
			
		||||
@ -316,7 +314,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        [HttpPost]
 | 
			
		||||
        public async Task<IActionResult> Create([FromBody] CreateProjectDto projectDto)
 | 
			
		||||
        {
 | 
			
		||||
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            if (!ModelState.IsValid)
 | 
			
		||||
            {
 | 
			
		||||
                var errors = ModelState.Values
 | 
			
		||||
@ -333,9 +330,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            _context.Projects.Add(project);
 | 
			
		||||
 | 
			
		||||
            await _context.SaveChangesAsync();
 | 
			
		||||
            var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Create_Project", Response = project.ToProjectDto() };
 | 
			
		||||
 | 
			
		||||
            await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
 | 
			
		||||
            return Ok(ApiResponse<object>.SuccessResponse(project.ToProjectDto(), "Success.", 200));
 | 
			
		||||
        }
 | 
			
		||||
@ -344,7 +338,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        [Route("update/{id}")]
 | 
			
		||||
        public async Task<IActionResult> Update([FromRoute] Guid id, [FromBody] UpdateProjectDto updateProjectDto)
 | 
			
		||||
        {
 | 
			
		||||
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            if (!ModelState.IsValid)
 | 
			
		||||
            {
 | 
			
		||||
                var errors = ModelState.Values
 | 
			
		||||
@ -363,10 +356,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
                await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
                var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Update_Project", Response = project.ToProjectDto() };
 | 
			
		||||
 | 
			
		||||
                await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
 | 
			
		||||
                return Ok(ApiResponse<object>.SuccessResponse(project.ToProjectDto(), "Success.", 200));
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
@ -376,6 +365,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //[HttpPost("assign-employee")]
 | 
			
		||||
        //public async Task<IActionResult> AssignEmployee(int? allocationid, int employeeId, int projectId)
 | 
			
		||||
        //{
 | 
			
		||||
@ -516,11 +506,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            if (projectAllocationDot != null)
 | 
			
		||||
            {
 | 
			
		||||
                Guid TenentID = GetTenantId();
 | 
			
		||||
                var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
 | 
			
		||||
                List<object>? result = new List<object>();
 | 
			
		||||
                List<Guid> employeeIds = new List<Guid>();
 | 
			
		||||
                List<Guid> projectIds = new List<Guid>();
 | 
			
		||||
 | 
			
		||||
                foreach (var item in projectAllocationDot)
 | 
			
		||||
                {
 | 
			
		||||
@ -549,9 +535,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                                projectAllocationFromDb.IsActive = false;
 | 
			
		||||
                                _context.Entry(projectAllocationFromDb).Property(e => e.ReAllocationDate).IsModified = true;
 | 
			
		||||
                                _context.Entry(projectAllocationFromDb).Property(e => e.IsActive).IsModified = true;
 | 
			
		||||
 | 
			
		||||
                                employeeIds.Add(projectAllocation.EmployeeId);
 | 
			
		||||
                                projectIds.Add(projectAllocation.ProjectId);
 | 
			
		||||
                            }
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
                            var result1 = new
 | 
			
		||||
@ -573,21 +556,15 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                            projectAllocation.IsActive = true;
 | 
			
		||||
                            _context.ProjectAllocations.Add(projectAllocation);
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
                            employeeIds.Add(projectAllocation.EmployeeId);
 | 
			
		||||
                            projectIds.Add(projectAllocation.ProjectId);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        return Ok(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Assign_Project", ProjectIds = projectIds, EmployeeList = employeeIds };
 | 
			
		||||
 | 
			
		||||
                await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
                return Ok(ApiResponse<object>.SuccessResponse(result, "Data saved successfully", 200));
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
@ -599,10 +576,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        public async Task<IActionResult> CreateProjectTask(List<WorkItemDot> workItemDot)
 | 
			
		||||
        {
 | 
			
		||||
            Guid tenantId = GetTenantId();
 | 
			
		||||
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            List<WorkItemVM> workItems = new List<WorkItemVM> { };
 | 
			
		||||
            List<Guid> projectIds = new List<Guid>();
 | 
			
		||||
            string message = "";
 | 
			
		||||
            string responseMessage = "";
 | 
			
		||||
            if (workItemDot != null)
 | 
			
		||||
            {
 | 
			
		||||
@ -610,25 +584,19 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                {
 | 
			
		||||
                    WorkItem workItem = item.ToWorkItemFromWorkItemDto(tenantId);
 | 
			
		||||
 | 
			
		||||
                    var workArea = await _context.WorkAreas.Include(a => a.Floor).FirstOrDefaultAsync(a => a.Id == workItem.WorkAreaId) ?? new WorkArea();
 | 
			
		||||
 | 
			
		||||
                    Building building = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == (workArea.Floor != null ? workArea.Floor.BuildingId : Guid.Empty)) ?? new Building();
 | 
			
		||||
 | 
			
		||||
                    if (item.Id != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        //update
 | 
			
		||||
                        _context.WorkItems.Update(workItem);
 | 
			
		||||
                        await _context.SaveChangesAsync();
 | 
			
		||||
                        responseMessage = "Task Updated Successfully";
 | 
			
		||||
                        message = $"Task Updated in Building: {building.Name}, on Floor: {workArea.Floor?.FloorName}, in Area: {workArea.AreaName} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}";
 | 
			
		||||
                        responseMessage = "Task Added Successfully";
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //create
 | 
			
		||||
                        _context.WorkItems.Add(workItem);
 | 
			
		||||
                        await _context.SaveChangesAsync();
 | 
			
		||||
                        responseMessage = "Task Added Successfully";
 | 
			
		||||
                        message = $"Task Added in Building: {building.Name}, on Floor: {workArea.Floor?.FloorName}, in Area: {workArea.AreaName} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}";
 | 
			
		||||
                        responseMessage = "Task Updated Successfully";
 | 
			
		||||
                    }
 | 
			
		||||
                    var result = new WorkItemVM
 | 
			
		||||
                    {
 | 
			
		||||
@ -636,14 +604,9 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                        WorkItem = workItem
 | 
			
		||||
                    };
 | 
			
		||||
                    workItems.Add(result);
 | 
			
		||||
                    projectIds.Add(building.ProjectId);
 | 
			
		||||
                }
 | 
			
		||||
                var activity = await _context.ActivityMasters.ToListAsync();
 | 
			
		||||
                var category = await _context.WorkCategoryMasters.ToListAsync();
 | 
			
		||||
 | 
			
		||||
                var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Infra", ProjectIds = projectIds, Message = message };
 | 
			
		||||
 | 
			
		||||
                await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
                return Ok(ApiResponse<object>.SuccessResponse(workItems, responseMessage, 200));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -655,9 +618,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        public async Task<IActionResult> DeleteProjectTask(Guid id)
 | 
			
		||||
        {
 | 
			
		||||
            Guid tenantId = _userHelper.GetTenantId();
 | 
			
		||||
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
            List<Guid> projectIds = new List<Guid>();
 | 
			
		||||
            WorkItem? task = await _context.WorkItems.AsNoTracking().Include(t => t.WorkArea).FirstOrDefaultAsync(t => t.Id == id && t.TenantId == tenantId);
 | 
			
		||||
            WorkItem? task = await _context.WorkItems.AsNoTracking().FirstOrDefaultAsync(t => t.Id == id && t.TenantId == tenantId);
 | 
			
		||||
            if (task != null)
 | 
			
		||||
            {
 | 
			
		||||
                if (task.CompletedWork == 0)
 | 
			
		||||
@ -668,15 +629,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                        _context.WorkItems.Remove(task);
 | 
			
		||||
                        await _context.SaveChangesAsync();
 | 
			
		||||
                        _logger.LogInfo("Task with ID {WorkItemId} has been successfully deleted.", task.Id);
 | 
			
		||||
 | 
			
		||||
                        var floorId = task.WorkArea?.FloorId;
 | 
			
		||||
                        var floor = await _context.Floor.Include(f => f.Building).FirstOrDefaultAsync(f => f.Id == floorId);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        projectIds.Add(floor?.Building?.ProjectId ?? Guid.Empty);
 | 
			
		||||
 | 
			
		||||
                        var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Infra", ProjectIds = projectIds, Message = $"Task Deleted in Building: {floor?.Building?.Name}, on Floor: {floor?.FloorName}, in Area: {task.WorkArea?.AreaName} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}" };
 | 
			
		||||
                        await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
@ -704,12 +656,8 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
        public async Task<IActionResult> ManageProjectInfra(List<InfraDot> infraDots)
 | 
			
		||||
        {
 | 
			
		||||
            Guid tenantId = GetTenantId();
 | 
			
		||||
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
 | 
			
		||||
            var responseData = new InfraVM { };
 | 
			
		||||
            string responseMessage = "";
 | 
			
		||||
            string message = "";
 | 
			
		||||
            List<Guid> projectIds = new List<Guid>();
 | 
			
		||||
            if (infraDots != null)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var item in infraDots)
 | 
			
		||||
@ -727,7 +675,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
                            responseData.building = building;
 | 
			
		||||
                            responseMessage = "Buliding Added Successfully";
 | 
			
		||||
                            message = "Building Added";
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
@ -736,10 +683,8 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
                            responseData.building = building;
 | 
			
		||||
                            responseMessage = "Buliding Updated Successfully";
 | 
			
		||||
                            message = "Building Updated";
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                        projectIds.Add(building.ProjectId);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (item.Floor != null)
 | 
			
		||||
                    {
 | 
			
		||||
@ -753,7 +698,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
                            responseData.floor = floor;
 | 
			
		||||
                            responseMessage = "Floor Added Successfully";
 | 
			
		||||
                            message = "Floor Added";
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
@ -762,11 +706,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
                            responseData.floor = floor;
 | 
			
		||||
                            responseMessage = "Floor Updated Successfully";
 | 
			
		||||
                            message = "Floor Updated";
 | 
			
		||||
                        }
 | 
			
		||||
                        Building? building = await _context.Buildings.FirstOrDefaultAsync(b => b.Id == floor.BuildingId);
 | 
			
		||||
                        projectIds.Add(building?.ProjectId ?? Guid.Empty);
 | 
			
		||||
                        message = $"{message} in Building: {building?.Name}";
 | 
			
		||||
                    }
 | 
			
		||||
                    if (item.WorkArea != null)
 | 
			
		||||
                    {
 | 
			
		||||
@ -780,7 +720,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
                            responseData.workArea = workArea;
 | 
			
		||||
                            responseMessage = "Work Area Added Successfully";
 | 
			
		||||
                            message = "Work Area Added";
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
@ -789,17 +728,9 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
                            responseData.workArea = workArea;
 | 
			
		||||
                            responseMessage = "Work Area Updated Successfully";
 | 
			
		||||
                            message = "Work Area Updated";
 | 
			
		||||
                        }
 | 
			
		||||
                        Floor? floor = await _context.Floor.Include(f => f.Building).FirstOrDefaultAsync(f => f.Id == workArea.FloorId);
 | 
			
		||||
                        projectIds.Add(floor?.Building?.ProjectId ?? Guid.Empty);
 | 
			
		||||
                        message = $"{message} in Building: {floor?.Building?.Name}, on Floor: {floor?.FloorName}";
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                message = $"{message} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}";
 | 
			
		||||
                var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Infra", ProjectIds = projectIds, Message = message };
 | 
			
		||||
 | 
			
		||||
                await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
                return Ok(ApiResponse<object>.SuccessResponse(responseData, responseMessage, 200));
 | 
			
		||||
            }
 | 
			
		||||
            return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "Infra Details are not valid.", 400));
 | 
			
		||||
@ -845,15 +776,16 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            return Ok(ApiResponse<object>.SuccessResponse(projects, "Success.", 200));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        [HttpPost("assign-projects/{employeeId}")]
 | 
			
		||||
        public async Task<ActionResult> AssigneProjectsToEmployee([FromBody] List<ProjectsAllocationDto> projectAllocationDtos, [FromRoute] Guid employeeId)
 | 
			
		||||
        {
 | 
			
		||||
            if (projectAllocationDtos != null && employeeId != Guid.Empty)
 | 
			
		||||
            {
 | 
			
		||||
                Guid TenentID = GetTenantId();
 | 
			
		||||
                var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
			
		||||
                List<object>? result = new List<object>();
 | 
			
		||||
                List<Guid> projectIds = new List<Guid>();
 | 
			
		||||
 | 
			
		||||
                foreach (var projectAllocationDto in projectAllocationDtos)
 | 
			
		||||
                {
 | 
			
		||||
@ -881,8 +813,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                                projectAllocationFromDb.IsActive = false;
 | 
			
		||||
                                _context.Entry(projectAllocationFromDb).Property(e => e.ReAllocationDate).IsModified = true;
 | 
			
		||||
                                _context.Entry(projectAllocationFromDb).Property(e => e.IsActive).IsModified = true;
 | 
			
		||||
 | 
			
		||||
                                projectIds.Add(projectAllocation.ProjectId);
 | 
			
		||||
                            }
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
                            var result1 = new
 | 
			
		||||
@ -905,8 +835,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                            _context.ProjectAllocations.Add(projectAllocation);
 | 
			
		||||
                            await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
                            projectIds.Add(projectAllocation.ProjectId);
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -917,9 +845,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                        return Ok(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Assign_Project", ProjectIds = projectIds, EmployeeId = employeeId };
 | 
			
		||||
 | 
			
		||||
                await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
 | 
			
		||||
 | 
			
		||||
                return Ok(ApiResponse<object>.SuccessResponse(result, "Data saved successfully", 200));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -8,13 +8,10 @@ using Marco.Pms.Model.Projects;
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
using Marco.Pms.Model.ViewModels.Activities;
 | 
			
		||||
using Marco.Pms.Model.ViewModels.Employee;
 | 
			
		||||
using Marco.Pms.Services.Service;
 | 
			
		||||
using MarcoBMS.Services.Helpers;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.CodeAnalysis;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Document = Marco.Pms.Model.DocumentManager.Document;
 | 
			
		||||
 | 
			
		||||
namespace MarcoBMS.Services.Controllers
 | 
			
		||||
{
 | 
			
		||||
@ -26,14 +23,12 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ApplicationDbContext _context;
 | 
			
		||||
        private readonly UserHelper _userHelper;
 | 
			
		||||
        private readonly S3UploadService _s3Service;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public TaskController(ApplicationDbContext context, UserHelper userHelper, S3UploadService s3Service)
 | 
			
		||||
        public TaskController(ApplicationDbContext context, UserHelper userHelper)
 | 
			
		||||
        {
 | 
			
		||||
            _context = context;
 | 
			
		||||
            _userHelper = userHelper;
 | 
			
		||||
            _s3Service = s3Service;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Guid GetTenantId()
 | 
			
		||||
@ -89,6 +84,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            return Ok(ApiResponse<object>.SuccessResponse(response, "Task assignned successfully", 200));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        [HttpPost("report")]
 | 
			
		||||
        public async Task<IActionResult> ReportTaskProgress([FromBody] ReportTaskDto reportTask)
 | 
			
		||||
        {
 | 
			
		||||
@ -108,13 +104,10 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
            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();
 | 
			
		||||
            if (taskAllocation == null || taskAllocation.WorkItem == null)
 | 
			
		||||
            if (taskAllocation == 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));
 | 
			
		||||
            if (taskAllocation.WorkItem != null)
 | 
			
		||||
            {
 | 
			
		||||
                if (taskAllocation.CompletedTask != 0)
 | 
			
		||||
@ -150,49 +143,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            _context.CheckListMappings.AddRange(checkListMappings);
 | 
			
		||||
            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);
 | 
			
		||||
            await _context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
@ -214,62 +164,10 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
            var tenantId = GetTenantId();
 | 
			
		||||
            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);
 | 
			
		||||
            _context.TaskComments.Add(comment);
 | 
			
		||||
            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();
 | 
			
		||||
            return Ok(ApiResponse<object>.SuccessResponse(response, "Comment saved successfully", 200));
 | 
			
		||||
        }
 | 
			
		||||
@ -317,13 +215,6 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
            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>();
 | 
			
		||||
            //foreach (var workItem in workItems)
 | 
			
		||||
            //{
 | 
			
		||||
@ -332,21 +223,10 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
 | 
			
		||||
                var response = taskAllocation.ToListTaskVMFromTaskAllocation();
 | 
			
		||||
 | 
			
		||||
                List<TaskComment> comments = allComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToList();
 | 
			
		||||
                List<TaskComment> comments = await _context.TaskComments.Where(c => c.TaskAllocationId == taskAllocation.Id).ToListAsync();
 | 
			
		||||
                List<BasicEmployeeVM> team = new List<BasicEmployeeVM>();
 | 
			
		||||
                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)
 | 
			
		||||
                {
 | 
			
		||||
                    var teamMember = employees.Find(e => e.Id == taskMember.EmployeeId);
 | 
			
		||||
@ -358,18 +238,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                List<CommentVM> commentVM = new List<CommentVM> { };
 | 
			
		||||
                foreach (var comment in comments)
 | 
			
		||||
                {
 | 
			
		||||
                    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);
 | 
			
		||||
                    commentVM.Add(comment.ToCommentVMFromTaskComment());
 | 
			
		||||
                }
 | 
			
		||||
                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();
 | 
			
		||||
@ -411,23 +280,8 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                if (taskAllocation == null) return NotFound(ApiResponse<object>.ErrorResponse("Task Not Found", "Task not found", 404));
 | 
			
		||||
                var taskVM = taskAllocation.TaskAllocationToTaskVM(employeeName);
 | 
			
		||||
                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 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)
 | 
			
		||||
                {
 | 
			
		||||
                    var result = member.Employee != null ? member.Employee.ToEmployeeVMFromEmployee() : new EmployeeVM();
 | 
			
		||||
@ -436,17 +290,7 @@ namespace MarcoBMS.Services.Controllers
 | 
			
		||||
                List<CommentVM> Comments = new List<CommentVM> { };
 | 
			
		||||
                foreach (var comment in comments)
 | 
			
		||||
                {
 | 
			
		||||
                    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);
 | 
			
		||||
                    Comments.Add(comment.ToCommentVMFromTaskComment());
 | 
			
		||||
                }
 | 
			
		||||
                taskVM.Comments = Comments;
 | 
			
		||||
                taskVM.TeamMembers = teamMembers;
 | 
			
		||||
 | 
			
		||||
@ -1,35 +0,0 @@
 | 
			
		||||
using MarcoBMS.Services.Service;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.SignalR;
 | 
			
		||||
 | 
			
		||||
namespace Marco.Pms.Services.Hubs
 | 
			
		||||
{
 | 
			
		||||
    [Authorize]
 | 
			
		||||
    public class MarcoHub : Hub
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ILoggingService _logger;
 | 
			
		||||
        public MarcoHub(ILoggingService logger)
 | 
			
		||||
        {
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
        }
 | 
			
		||||
        public async Task SendMessage(string user, string message)
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogInfo($"User: {user} Message : {message}");
 | 
			
		||||
            await Clients.All.SendAsync("ReceiveMessage", user, message);
 | 
			
		||||
        }
 | 
			
		||||
        public override async Task OnConnectedAsync()
 | 
			
		||||
        {
 | 
			
		||||
            await base.OnConnectedAsync();
 | 
			
		||||
            _logger.LogInfo($"Connected successfully");
 | 
			
		||||
            await Clients.All.SendAsync("Connected successfully");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Optional: override OnDisconnectedAsync
 | 
			
		||||
        public override async Task OnDisconnectedAsync(Exception? exception)
 | 
			
		||||
        {
 | 
			
		||||
            await base.OnDisconnectedAsync(exception);
 | 
			
		||||
            _logger.LogInfo($"DIsonnected successfully");
 | 
			
		||||
            await Clients.All.SendAsync("Disonnected successfully");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -17,7 +17,6 @@
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.12" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.12" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.7" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
 | 
			
		||||
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.12">
 | 
			
		||||
      <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,6 @@ using Marco.Pms.Model.Authentication;
 | 
			
		||||
using Marco.Pms.Model.Entitlements;
 | 
			
		||||
using Marco.Pms.Model.Utilities;
 | 
			
		||||
using Marco.Pms.Services.Helpers;
 | 
			
		||||
using Marco.Pms.Services.Hubs;
 | 
			
		||||
using Marco.Pms.Services.Service;
 | 
			
		||||
using MarcoBMS.Services.Helpers;
 | 
			
		||||
using MarcoBMS.Services.Middleware;
 | 
			
		||||
@ -60,8 +59,7 @@ builder.Services.AddCors(options =>
 | 
			
		||||
    {
 | 
			
		||||
        policy.AllowAnyOrigin()
 | 
			
		||||
              .AllowAnyMethod()
 | 
			
		||||
              .AllowAnyHeader()
 | 
			
		||||
              .WithExposedHeaders("Authorization");
 | 
			
		||||
              .AllowAnyHeader();
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -163,28 +161,10 @@ if (jwtSettings != null && jwtSettings.Key != null)
 | 
			
		||||
            ValidAudience = jwtSettings.Audience,
 | 
			
		||||
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Key))
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        options.Events = new JwtBearerEvents
 | 
			
		||||
        {
 | 
			
		||||
            OnMessageReceived = context =>
 | 
			
		||||
            {
 | 
			
		||||
                var accessToken = context.Request.Query["access_token"];
 | 
			
		||||
                var path = context.HttpContext.Request.Path;
 | 
			
		||||
 | 
			
		||||
                // Match your hub route here
 | 
			
		||||
                if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs/marco"))
 | 
			
		||||
                {
 | 
			
		||||
                    context.Token = accessToken;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
    builder.Services.AddSingleton(jwtSettings);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
builder.Services.AddSignalR();
 | 
			
		||||
builder.WebHost.ConfigureKestrel(options =>
 | 
			
		||||
{
 | 
			
		||||
    options.AddServerHeader = false; // Disable the "Server" header
 | 
			
		||||
@ -227,7 +207,7 @@ app.UseHttpsRedirection();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
app.UseAuthorization();
 | 
			
		||||
app.MapHub<MarcoHub>("/hubs/marco");
 | 
			
		||||
 | 
			
		||||
app.MapControllers();
 | 
			
		||||
 | 
			
		||||
app.Run();
 | 
			
		||||
 | 
			
		||||
@ -28,46 +28,53 @@ namespace Marco.Pms.Services.Service
 | 
			
		||||
            _s3Client = new AmazonS3Client(settings.AccessKey, settings.SecretKey, region);
 | 
			
		||||
        }
 | 
			
		||||
        //public async Task<string> UploadFileAsync(string fileName, string contentType)
 | 
			
		||||
        public async Task UploadFileAsync(string base64, string fileType, string objectKey)
 | 
			
		||||
        public async Task<string> UploadFileAsync(string base64Data, Guid tenantId, string tag)
 | 
			
		||||
        {
 | 
			
		||||
            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")
 | 
			
		||||
                                                .GetChildren()
 | 
			
		||||
                                                .Select(x => x.Value)
 | 
			
		||||
                                                .ToList();
 | 
			
		||||
 | 
			
		||||
            if (allowedFilesType == null || !allowedFilesType.Contains(fileType))
 | 
			
		||||
            string fileType = GetContentTypeFromBase64(base64);
 | 
			
		||||
            if (allowedFilesType != null && allowedFilesType.Contains(fileType))
 | 
			
		||||
            {
 | 
			
		||||
                throw new InvalidOperationException("Unsupported file type.");
 | 
			
		||||
                string fileName = GenerateFileName(fileType, tenantId, tag);
 | 
			
		||||
 | 
			
		||||
                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;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            throw new InvalidOperationException("Unsupported file type.");
 | 
			
		||||
        }
 | 
			
		||||
        public string GeneratePreSignedUrlAsync(string objectKey)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@
 | 
			
		||||
        "Title": "Dev"
 | 
			
		||||
    },
 | 
			
		||||
    "ConnectionStrings": {
 | 
			
		||||
        "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMS1"
 | 
			
		||||
        "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMSGuid"
 | 
			
		||||
    },
 | 
			
		||||
    "SmtpSettings": {
 | 
			
		||||
        "SmtpServer": "smtp.gmail.com",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user