Compare commits

...

32 Commits

Author SHA1 Message Date
2ed0e6e5b6 Merge branch 'Issues_June_3W' of https://git.marcoaiot.com/admin/marco.pms.api into Issues_June_3W 2025-06-23 17:10:10 +05:30
72a92417b5 Implemented An API to get list of all notes 2025-06-23 17:10:05 +05:30
3dfde6d9a5 Added new entry in status master table 2025-06-19 11:43:10 +05:30
4164c7d761 Cheange the message sending when manageing project infrastructure 2025-06-18 18:12:28 +05:30
303f326773 Implemented signalR in Employee module 2025-06-18 12:00:35 +05:30
e2956c0c8c Added signalR functionality in Project Infrastructure 2025-06-17 17:05:52 +05:30
39378f3a88 Resolved rebase issues 2025-06-16 18:09:00 +05:30
caacb43aa8 Merge branch 'SingalR_Integration' of https://git.marcoaiot.com/admin/marco.pms.api into SingalR_Integration 2025-06-16 17:57:49 +05:30
29ea1698bc Implemented signalR in manage Task API 2025-06-16 17:57:44 +05:30
793877b8f8 Implemented signalR in project infrastructure APIs 2025-06-16 17:57:37 +05:30
f47586710b Add basic implementation and add in record attendance API for testing 2025-06-16 17:55:59 +05:30
b21d30c18e Changed the keyword for updating and creating project 2025-06-16 17:54:52 +05:30
d78a2fe3b2 Implemented signalR in project infrastructure APIs 2025-06-16 17:54:52 +05:30
6ebc74499f Add basic implementation and add in record attendance API for testing 2025-06-16 17:54:52 +05:30
17ae02a0b3 Implemented signalR in manage Task API 2025-06-13 16:22:58 +05:30
9d5535edf1 Added description in list task API 2025-06-12 22:30:43 +05:30
c689f2dfd8 Merge branch 'Acititvity_Images' of https://git.marcoaiot.com/admin/marco.pms.api into Acititvity_Images 2025-06-12 21:40:36 +05:30
5c019a2ff6 Chnaged the name form presignedUrls to ReportedProsignedUrl 2025-06-12 21:40:31 +05:30
cb185db4f3 Sending pre signed url in task list API 2025-06-12 21:40:31 +05:30
1a51860517 Implemented the image capture while writing comment in task allocation and add presignedurl in task get by in API 2025-06-12 21:40:31 +05:30
f275d08215 Implemented image storing to S3 while reporting task 2025-06-12 21:40:31 +05:30
4ccc690560 Chnaged the name form presignedUrls to ReportedProsignedUrl 2025-06-12 21:35:40 +05:30
82f3fdbc23 Merge branch 'SingalR_Integration' of https://git.marcoaiot.com/admin/marco.pms.api into SingalR_Integration 2025-06-12 20:54:06 +05:30
790e9f63e1 Changed the keyword for updating and creating project 2025-06-12 20:54:00 +05:30
0636c8aedd Implemented signalR in project infrastructure APIs 2025-06-12 20:54:00 +05:30
8f2c828282 Add basic implementation and add in record attendance API for testing 2025-06-12 20:54:00 +05:30
169e7f6601 Changed the keyword for updating and creating project 2025-06-12 20:53:24 +05:30
691a670a28 Sending pre signed url in task list API 2025-06-12 18:32:50 +05:30
76b6ac6581 Implemented the image capture while writing comment in task allocation and add presignedurl in task get by in API 2025-06-12 17:02:18 +05:30
abe7870ad5 Implemented image storing to S3 while reporting task 2025-06-12 16:09:28 +05:30
712c5e2a0a Implemented signalR in project infrastructure APIs 2025-06-12 11:04:24 +05:30
8814dc59d9 Add basic implementation and add in record attendance API for testing 2025-06-11 10:21:39 +05:30
27 changed files with 7158 additions and 103 deletions

View File

@ -44,6 +44,7 @@ namespace Marco.Pms.DataAccess.Data
public DbSet<TaskAllocation> TaskAllocations { get; set; } public DbSet<TaskAllocation> TaskAllocations { get; set; }
public DbSet<TaskComment> TaskComments { get; set; } public DbSet<TaskComment> TaskComments { get; set; }
public DbSet<TaskMembers> TaskMembers { get; set; } public DbSet<TaskMembers> TaskMembers { get; set; }
public DbSet<TaskAttachment> TaskAttachments { get; set; }
public DbSet<Attendance> Attendes { get; set; } public DbSet<Attendance> Attendes { get; set; }
public DbSet<AttendanceLog> AttendanceLogs { get; set; } public DbSet<AttendanceLog> AttendanceLogs { get; set; }
public DbSet<Employee> Employees { get; set; } public DbSet<Employee> Employees { get; set; }
@ -160,14 +161,19 @@ namespace Marco.Pms.DataAccess.Data
}, },
new StatusMaster new StatusMaster
{ {
Id = new Guid("603e994b-a27f-4e5d-a251-f3d69b0498ba"), Id = new Guid("cdad86aa-8a56-4ff4-b633-9c629057dfef"),
Status = "In Progress", Status = "In Progress",
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
}, new StatusMaster
{
Id = new Guid("603e994b-a27f-4e5d-a251-f3d69b0498ba"),
Status = "On Hold",
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
}, },
new StatusMaster new StatusMaster
{ {
Id = new Guid("ef1c356e-0fe0-42df-a5d3-8daee355492d"), Id = new Guid("ef1c356e-0fe0-42df-a5d3-8daee355492d"),
Status = "On Hold", Status = "In Active",
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26") TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
}, },
new StatusMaster new StatusMaster

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
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");
}
}
}

View File

@ -63,6 +63,23 @@ namespace Marco.Pms.DataAccess.Migrations
b.ToTable("TaskAllocations"); b.ToTable("TaskAllocations");
}); });
modelBuilder.Entity("Marco.Pms.Model.Activities.TaskAttachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("DocumentId")
.HasColumnType("char(36)");
b.Property<Guid>("ReferenceId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.ToTable("TaskAttachments");
});
modelBuilder.Entity("Marco.Pms.Model.Activities.TaskComment", b => modelBuilder.Entity("Marco.Pms.Model.Activities.TaskComment", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -1658,17 +1675,23 @@ namespace Marco.Pms.DataAccess.Migrations
}, },
new new
{ {
Id = new Guid("603e994b-a27f-4e5d-a251-f3d69b0498ba"), Id = new Guid("cdad86aa-8a56-4ff4-b633-9c629057dfef"),
Status = "In Progress", Status = "In Progress",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26") TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
}, },
new new
{ {
Id = new Guid("ef1c356e-0fe0-42df-a5d3-8daee355492d"), Id = new Guid("603e994b-a27f-4e5d-a251-f3d69b0498ba"),
Status = "On Hold", Status = "On Hold",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26") TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
}, },
new new
{
Id = new Guid("ef1c356e-0fe0-42df-a5d3-8daee355492d"),
Status = "In Active",
TenantId = new Guid("b3466e83-7e11-464c-b93a-daf047838b26")
},
new
{ {
Id = new Guid("33deaef9-9af1-4f2a-b443-681ea0d04f81"), Id = new Guid("33deaef9-9af1-4f2a-b443-681ea0d04f81"),
Status = "Completed", Status = "Completed",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,11 +7,13 @@ using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.AttendanceVM; using Marco.Pms.Model.ViewModels.AttendanceVM;
using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service; using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Document = Marco.Pms.Model.DocumentManager.Document; using Document = Marco.Pms.Model.DocumentManager.Document;
@ -30,10 +32,11 @@ namespace MarcoBMS.Services.Controllers
private readonly S3UploadService _s3Service; private readonly S3UploadService _s3Service;
private readonly PermissionServices _permission; private readonly PermissionServices _permission;
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
private readonly IHubContext<MarcoHub> _signalR;
public AttendanceController( public AttendanceController(
ApplicationDbContext context, EmployeeHelper employeeHelper, ProjectsHelper projectsHelper, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permission) ApplicationDbContext context, EmployeeHelper employeeHelper, ProjectsHelper projectsHelper, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permission, IHubContext<MarcoHub> signalR)
{ {
_context = context; _context = context;
_employeeHelper = employeeHelper; _employeeHelper = employeeHelper;
@ -42,6 +45,7 @@ namespace MarcoBMS.Services.Controllers
_s3Service = s3Service; _s3Service = s3Service;
_logger = logger; _logger = logger;
_permission = permission; _permission = permission;
_signalR = signalR;
} }
private Guid GetTenantId() private Guid GetTenantId()
@ -558,6 +562,13 @@ namespace MarcoBMS.Services.Controllers
Activity = attendance.Activity, Activity = attendance.Activity,
JobRoleName = employee.JobRole.Name 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); _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)); return Ok(ApiResponse<object>.SuccessResponse(vm, "Attendance marked successfully.", 200));
} }
@ -688,7 +699,6 @@ namespace MarcoBMS.Services.Controllers
} }
Document? document = null; Document? document = null;
var Image = recordAttendanceDot.Image; var Image = recordAttendanceDot.Image;
var objectKey = string.Empty;
var preSignedUrl = string.Empty; var preSignedUrl = string.Empty;
if (Image != null && Image.ContentType != null) if (Image != null && Image.ContentType != null)
@ -697,7 +707,16 @@ namespace MarcoBMS.Services.Controllers
if (string.IsNullOrEmpty(Image.Base64Data)) if (string.IsNullOrEmpty(Image.Base64Data))
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, TenantId, "attendance"); //If base64 has a data URI prefix, strip it
var base64 = Image.Base64Data.Contains(",")
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
: Image.Base64Data;
string fileType = _s3Service.GetContentTypeFromBase64(base64);
string fileName = _s3Service.GenerateFileName(fileType, TenantId, "attendance");
string objectKey = $"tenant-{TenantId}/Employee/{recordAttendanceDot.EmployeeID}/Attendance/{fileName}";
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(objectKey); preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(objectKey);
document = new Document document = new Document
@ -712,6 +731,7 @@ namespace MarcoBMS.Services.Controllers
}; };
_context.Documents.Add(document); _context.Documents.Add(document);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
} }

View File

@ -9,11 +9,13 @@ using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Employee; using Marco.Pms.Model.ViewModels.Employee;
using Marco.Pms.Services.Hubs;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace MarcoBMS.Services.Controllers namespace MarcoBMS.Services.Controllers
@ -32,9 +34,11 @@ namespace MarcoBMS.Services.Controllers
private readonly UserHelper _userHelper; private readonly UserHelper _userHelper;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
private readonly IHubContext<MarcoHub> _signalR;
public EmployeeController(UserManager<ApplicationUser> userManager, IEmailSender emailSender, public EmployeeController(UserManager<ApplicationUser> userManager, IEmailSender emailSender,
ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger) ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger,
IHubContext<MarcoHub> signalR)
{ {
_context = context; _context = context;
_userManager = userManager; _userManager = userManager;
@ -43,6 +47,7 @@ namespace MarcoBMS.Services.Controllers
_userHelper = userHelper; _userHelper = userHelper;
_configuration = configuration; _configuration = configuration;
_logger = logger; _logger = logger;
_signalR = signalR;
} }
[HttpGet] [HttpGet]
@ -154,6 +159,8 @@ namespace MarcoBMS.Services.Controllers
public async Task<IActionResult> CreateUser([FromBody] CreateUserDto model) public async Task<IActionResult> CreateUser([FromBody] CreateUserDto model)
{ {
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
Guid employeeId = Guid.Empty;
if (model == null) if (model == null)
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", "Invaild Data", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", "Invaild Data", 400));
@ -180,6 +187,7 @@ namespace MarcoBMS.Services.Controllers
_context.Employees.Update(existingEmployee); _context.Employees.Update(existingEmployee);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
employeeId = existingEmployee.Id;
responsemessage = "User updated successfully."; responsemessage = "User updated successfully.";
} }
else else
@ -214,7 +222,7 @@ namespace MarcoBMS.Services.Controllers
_context.Employees.Add(newEmployee); _context.Employees.Add(newEmployee);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
employeeId = newEmployee.Id;
/* SEND USER REGISTRATION MAIL*/ /* SEND USER REGISTRATION MAIL*/
var token = await _userManager.GeneratePasswordResetTokenAsync(user); var token = await _userManager.GeneratePasswordResetTokenAsync(user);
@ -233,6 +241,7 @@ namespace MarcoBMS.Services.Controllers
_context.Employees.Update(existingEmployee); _context.Employees.Update(existingEmployee);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
employeeId = existingEmployee.Id;
/* SEND USER REGISTRATION MAIL*/ /* SEND USER REGISTRATION MAIL*/
var token = await _userManager.GeneratePasswordResetTokenAsync(user); var token = await _userManager.GeneratePasswordResetTokenAsync(user);
@ -256,17 +265,22 @@ namespace MarcoBMS.Services.Controllers
existingEmployee = GetUpdateEmployeeModel(model, existingEmployee); existingEmployee = GetUpdateEmployeeModel(model, existingEmployee);
_context.Employees.Update(existingEmployee); _context.Employees.Update(existingEmployee);
responsemessage = "User updated successfully."; responsemessage = "User updated successfully.";
employeeId = existingEmployee.Id;
} }
else else
{ {
// Create Employee record if missing // Create Employee record if missing
Employee newEmployee = GetNewEmployeeModel(model, tenantId, string.Empty); Employee newEmployee = GetNewEmployeeModel(model, tenantId, string.Empty);
_context.Employees.Add(newEmployee); _context.Employees.Add(newEmployee);
employeeId = newEmployee.Id;
} }
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
responsemessage = "User created successfully."; 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)); return Ok(ApiResponse<object>.SuccessResponse("Success.", responsemessage, 200));
} }
@ -420,6 +434,9 @@ namespace MarcoBMS.Services.Controllers
} }
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
_logger.LogInfo("Employee with ID {EmployeId} Deleted successfully", employee.Id); _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 else

View File

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

View File

@ -749,11 +749,11 @@ namespace Marco.Pms.Services.Controllers
return Ok(response); return Ok(response);
} }
[HttpGet("contact-tag/{id}")] //[HttpGet("contact-tag/{id}")]
public async Task<IActionResult> GetContactTagMaster(Guid id) //public async Task<IActionResult> GetContactTagMaster(Guid id)
{ //{
return Ok(); // return Ok();
} //}
[HttpPost("contact-tag")] [HttpPost("contact-tag")]
public async Task<IActionResult> CreateContactTagMaster([FromBody] CreateContactTagDto contactTagDto) public async Task<IActionResult> CreateContactTagMaster([FromBody] CreateContactTagDto contactTagDto)

View File

@ -8,12 +8,13 @@ using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Employee; using Marco.Pms.Model.ViewModels.Employee;
using Marco.Pms.Model.ViewModels.Projects; using Marco.Pms.Model.ViewModels.Projects;
using Marco.Pms.Services.Hubs;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace MarcoBMS.Services.Controllers namespace MarcoBMS.Services.Controllers
{ {
@ -27,15 +28,18 @@ namespace MarcoBMS.Services.Controllers
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
private readonly RolesHelper _rolesHelper; private readonly RolesHelper _rolesHelper;
private readonly ProjectsHelper _projectsHelper; private readonly ProjectsHelper _projectsHelper;
private readonly IHubContext<MarcoHub> _signalR;
public ProjectController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, RolesHelper rolesHelper, ProjectsHelper projectHelper) public ProjectController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, RolesHelper rolesHelper, ProjectsHelper projectHelper, IHubContext<MarcoHub> signalR)
{ {
_context = context; _context = context;
_userHelper = userHelper; _userHelper = userHelper;
_logger = logger; _logger = logger;
_rolesHelper = rolesHelper; _rolesHelper = rolesHelper;
_projectsHelper = projectHelper; _projectsHelper = projectHelper;
_signalR = signalR;
} }
[HttpGet("list/basic")] [HttpGet("list/basic")]
@ -60,7 +64,7 @@ namespace MarcoBMS.Services.Controllers
} }
List<Project> projects = await _projectsHelper.GetMyProjects(tenantId, LoggedInEmployee); List<Project> projects = await _projectsHelper.GetMyProjects(tenantId, LoggedInEmployee);
// 4. Project projection to ProjectInfoVM // 4. Project projection to ProjectInfoVM
@ -84,8 +88,6 @@ namespace MarcoBMS.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(response, "Success.", 200)); return Ok(ApiResponse<object>.SuccessResponse(response, "Success.", 200));
} }
[HttpGet("list")] [HttpGet("list")]
public async Task<IActionResult> GetAll() public async Task<IActionResult> GetAll()
{ {
@ -314,6 +316,7 @@ namespace MarcoBMS.Services.Controllers
[HttpPost] [HttpPost]
public async Task<IActionResult> Create([FromBody] CreateProjectDto projectDto) public async Task<IActionResult> Create([FromBody] CreateProjectDto projectDto)
{ {
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
var errors = ModelState.Values var errors = ModelState.Values
@ -330,6 +333,9 @@ namespace MarcoBMS.Services.Controllers
_context.Projects.Add(project); _context.Projects.Add(project);
await _context.SaveChangesAsync(); 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)); return Ok(ApiResponse<object>.SuccessResponse(project.ToProjectDto(), "Success.", 200));
} }
@ -338,6 +344,7 @@ namespace MarcoBMS.Services.Controllers
[Route("update/{id}")] [Route("update/{id}")]
public async Task<IActionResult> Update([FromRoute] Guid id, [FromBody] UpdateProjectDto updateProjectDto) public async Task<IActionResult> Update([FromRoute] Guid id, [FromBody] UpdateProjectDto updateProjectDto)
{ {
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
var errors = ModelState.Values var errors = ModelState.Values
@ -356,6 +363,10 @@ namespace MarcoBMS.Services.Controllers
await _context.SaveChangesAsync(); 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)); return Ok(ApiResponse<object>.SuccessResponse(project.ToProjectDto(), "Success.", 200));
} }
@ -365,7 +376,6 @@ namespace MarcoBMS.Services.Controllers
} }
} }
//[HttpPost("assign-employee")] //[HttpPost("assign-employee")]
//public async Task<IActionResult> AssignEmployee(int? allocationid, int employeeId, int projectId) //public async Task<IActionResult> AssignEmployee(int? allocationid, int employeeId, int projectId)
//{ //{
@ -506,7 +516,11 @@ namespace MarcoBMS.Services.Controllers
if (projectAllocationDot != null) if (projectAllocationDot != null)
{ {
Guid TenentID = GetTenantId(); Guid TenentID = GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
List<object>? result = new List<object>(); List<object>? result = new List<object>();
List<Guid> employeeIds = new List<Guid>();
List<Guid> projectIds = new List<Guid>();
foreach (var item in projectAllocationDot) foreach (var item in projectAllocationDot)
{ {
@ -535,6 +549,9 @@ namespace MarcoBMS.Services.Controllers
projectAllocationFromDb.IsActive = false; projectAllocationFromDb.IsActive = false;
_context.Entry(projectAllocationFromDb).Property(e => e.ReAllocationDate).IsModified = true; _context.Entry(projectAllocationFromDb).Property(e => e.ReAllocationDate).IsModified = true;
_context.Entry(projectAllocationFromDb).Property(e => e.IsActive).IsModified = true; _context.Entry(projectAllocationFromDb).Property(e => e.IsActive).IsModified = true;
employeeIds.Add(projectAllocation.EmployeeId);
projectIds.Add(projectAllocation.ProjectId);
} }
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
var result1 = new var result1 = new
@ -556,15 +573,21 @@ namespace MarcoBMS.Services.Controllers
projectAllocation.IsActive = true; projectAllocation.IsActive = true;
_context.ProjectAllocations.Add(projectAllocation); _context.ProjectAllocations.Add(projectAllocation);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
employeeIds.Add(projectAllocation.EmployeeId);
projectIds.Add(projectAllocation.ProjectId);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
return Ok(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400)); 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)); return Ok(ApiResponse<object>.SuccessResponse(result, "Data saved successfully", 200));
} }
@ -576,7 +599,10 @@ namespace MarcoBMS.Services.Controllers
public async Task<IActionResult> CreateProjectTask(List<WorkItemDot> workItemDot) public async Task<IActionResult> CreateProjectTask(List<WorkItemDot> workItemDot)
{ {
Guid tenantId = GetTenantId(); Guid tenantId = GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
List<WorkItemVM> workItems = new List<WorkItemVM> { }; List<WorkItemVM> workItems = new List<WorkItemVM> { };
List<Guid> projectIds = new List<Guid>();
string message = "";
string responseMessage = ""; string responseMessage = "";
if (workItemDot != null) if (workItemDot != null)
{ {
@ -584,19 +610,25 @@ namespace MarcoBMS.Services.Controllers
{ {
WorkItem workItem = item.ToWorkItemFromWorkItemDto(tenantId); 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) if (item.Id != null)
{ {
//update //update
_context.WorkItems.Update(workItem); _context.WorkItems.Update(workItem);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
responseMessage = "Task Added Successfully"; 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}";
} }
else else
{ {
//create //create
_context.WorkItems.Add(workItem); _context.WorkItems.Add(workItem);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
responseMessage = "Task Updated Successfully"; 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}";
} }
var result = new WorkItemVM var result = new WorkItemVM
{ {
@ -604,9 +636,14 @@ namespace MarcoBMS.Services.Controllers
WorkItem = workItem WorkItem = workItem
}; };
workItems.Add(result); workItems.Add(result);
projectIds.Add(building.ProjectId);
} }
var activity = await _context.ActivityMasters.ToListAsync(); var activity = await _context.ActivityMasters.ToListAsync();
var category = await _context.WorkCategoryMasters.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)); return Ok(ApiResponse<object>.SuccessResponse(workItems, responseMessage, 200));
} }
@ -618,7 +655,9 @@ namespace MarcoBMS.Services.Controllers
public async Task<IActionResult> DeleteProjectTask(Guid id) public async Task<IActionResult> DeleteProjectTask(Guid id)
{ {
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
WorkItem? task = await _context.WorkItems.AsNoTracking().FirstOrDefaultAsync(t => t.Id == id && t.TenantId == tenantId); 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);
if (task != null) if (task != null)
{ {
if (task.CompletedWork == 0) if (task.CompletedWork == 0)
@ -629,6 +668,15 @@ namespace MarcoBMS.Services.Controllers
_context.WorkItems.Remove(task); _context.WorkItems.Remove(task);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
_logger.LogInfo("Task with ID {WorkItemId} has been successfully deleted.", task.Id); _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 else
{ {
@ -656,8 +704,12 @@ namespace MarcoBMS.Services.Controllers
public async Task<IActionResult> ManageProjectInfra(List<InfraDot> infraDots) public async Task<IActionResult> ManageProjectInfra(List<InfraDot> infraDots)
{ {
Guid tenantId = GetTenantId(); Guid tenantId = GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var responseData = new InfraVM { }; var responseData = new InfraVM { };
string responseMessage = ""; string responseMessage = "";
string message = "";
List<Guid> projectIds = new List<Guid>();
if (infraDots != null) if (infraDots != null)
{ {
foreach (var item in infraDots) foreach (var item in infraDots)
@ -675,6 +727,7 @@ namespace MarcoBMS.Services.Controllers
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
responseData.building = building; responseData.building = building;
responseMessage = "Buliding Added Successfully"; responseMessage = "Buliding Added Successfully";
message = "Building Added";
} }
else else
{ {
@ -683,8 +736,10 @@ namespace MarcoBMS.Services.Controllers
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
responseData.building = building; responseData.building = building;
responseMessage = "Buliding Updated Successfully"; responseMessage = "Buliding Updated Successfully";
message = "Building Updated";
} }
projectIds.Add(building.ProjectId);
} }
if (item.Floor != null) if (item.Floor != null)
{ {
@ -698,6 +753,7 @@ namespace MarcoBMS.Services.Controllers
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
responseData.floor = floor; responseData.floor = floor;
responseMessage = "Floor Added Successfully"; responseMessage = "Floor Added Successfully";
message = "Floor Added";
} }
else else
{ {
@ -706,7 +762,11 @@ namespace MarcoBMS.Services.Controllers
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
responseData.floor = floor; responseData.floor = floor;
responseMessage = "Floor Updated Successfully"; 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) if (item.WorkArea != null)
{ {
@ -720,6 +780,7 @@ namespace MarcoBMS.Services.Controllers
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
responseData.workArea = workArea; responseData.workArea = workArea;
responseMessage = "Work Area Added Successfully"; responseMessage = "Work Area Added Successfully";
message = "Work Area Added";
} }
else else
{ {
@ -728,9 +789,17 @@ namespace MarcoBMS.Services.Controllers
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
responseData.workArea = workArea; responseData.workArea = workArea;
responseMessage = "Work Area Updated Successfully"; 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 Ok(ApiResponse<object>.SuccessResponse(responseData, responseMessage, 200));
} }
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "Infra Details are not valid.", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "Infra Details are not valid.", 400));
@ -776,16 +845,15 @@ namespace MarcoBMS.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(projects, "Success.", 200)); return Ok(ApiResponse<object>.SuccessResponse(projects, "Success.", 200));
} }
[HttpPost("assign-projects/{employeeId}")] [HttpPost("assign-projects/{employeeId}")]
public async Task<ActionResult> AssigneProjectsToEmployee([FromBody] List<ProjectsAllocationDto> projectAllocationDtos, [FromRoute] Guid employeeId) public async Task<ActionResult> AssigneProjectsToEmployee([FromBody] List<ProjectsAllocationDto> projectAllocationDtos, [FromRoute] Guid employeeId)
{ {
if (projectAllocationDtos != null && employeeId != Guid.Empty) if (projectAllocationDtos != null && employeeId != Guid.Empty)
{ {
Guid TenentID = GetTenantId(); Guid TenentID = GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
List<object>? result = new List<object>(); List<object>? result = new List<object>();
List<Guid> projectIds = new List<Guid>();
foreach (var projectAllocationDto in projectAllocationDtos) foreach (var projectAllocationDto in projectAllocationDtos)
{ {
@ -813,6 +881,8 @@ namespace MarcoBMS.Services.Controllers
projectAllocationFromDb.IsActive = false; projectAllocationFromDb.IsActive = false;
_context.Entry(projectAllocationFromDb).Property(e => e.ReAllocationDate).IsModified = true; _context.Entry(projectAllocationFromDb).Property(e => e.ReAllocationDate).IsModified = true;
_context.Entry(projectAllocationFromDb).Property(e => e.IsActive).IsModified = true; _context.Entry(projectAllocationFromDb).Property(e => e.IsActive).IsModified = true;
projectIds.Add(projectAllocation.ProjectId);
} }
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
var result1 = new var result1 = new
@ -835,6 +905,8 @@ namespace MarcoBMS.Services.Controllers
_context.ProjectAllocations.Add(projectAllocation); _context.ProjectAllocations.Add(projectAllocation);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
projectIds.Add(projectAllocation.ProjectId);
} }
@ -845,6 +917,9 @@ namespace MarcoBMS.Services.Controllers
return Ok(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400)); 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)); return Ok(ApiResponse<object>.SuccessResponse(result, "Data saved successfully", 200));
} }

View File

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

View File

@ -0,0 +1,35 @@
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");
}
}
}

View File

@ -17,6 +17,7 @@
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.12" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" 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.OpenApi" Version="7.0.7" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.12"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.12">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -4,6 +4,7 @@ using Marco.Pms.Model.Authentication;
using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Helpers;
using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service; using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Middleware; using MarcoBMS.Services.Middleware;
@ -59,7 +60,8 @@ builder.Services.AddCors(options =>
{ {
policy.AllowAnyOrigin() policy.AllowAnyOrigin()
.AllowAnyMethod() .AllowAnyMethod()
.AllowAnyHeader(); .AllowAnyHeader()
.WithExposedHeaders("Authorization");
}); });
}); });
@ -161,10 +163,28 @@ if (jwtSettings != null && jwtSettings.Key != null)
ValidAudience = jwtSettings.Audience, ValidAudience = jwtSettings.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Key)) 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.AddSingleton(jwtSettings);
} }
builder.Services.AddSignalR();
builder.WebHost.ConfigureKestrel(options => builder.WebHost.ConfigureKestrel(options =>
{ {
options.AddServerHeader = false; // Disable the "Server" header options.AddServerHeader = false; // Disable the "Server" header
@ -207,7 +227,7 @@ app.UseHttpsRedirection();
app.UseAuthorization(); app.UseAuthorization();
app.MapHub<MarcoHub>("/hubs/marco");
app.MapControllers(); app.MapControllers();
app.Run(); app.Run();

View File

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

View File

@ -9,7 +9,7 @@
"Title": "Dev" "Title": "Dev"
}, },
"ConnectionStrings": { "ConnectionStrings": {
"DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMSGuid" "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMS1"
}, },
"SmtpSettings": { "SmtpSettings": {
"SmtpServer": "smtp.gmail.com", "SmtpServer": "smtp.gmail.com",